В большинстве языков программирования (если не во всех) вам нужно объявлять переменные. Например, в C #, если это числовое поле, то
int PhoneNumber
Если я использую нормальный английский язык, мне не нужно указывать, PhoneNumber
как int
его использовать. Например, если я попрошу моего друга Сэма дать мне свой номер телефона, я скажу:
"Сэм, дай мне номер телефона"
Я бы не сказал
"Char (20) Сэм, дай мне номер телефона"
Почему мы вообще должны указывать тип данных?
programming-languages
Дик Смит
источник
источник
Ответы:
Это два независимых вопроса:
Кстати, ответ на оба вопроса: мы не делаем.
Есть много статически типизированных языков программирования, где вам не нужно объявлять типы. Компилятор может вывести типы из окружающего контекста и использования.
Например, в Scala вы можете сказать
или вы могли бы просто сказать
Они в точности эквивалентны: компилятор выведет тип
Int
из выражения инициализации23
.Аналогично, в C♯ вы можете сказать любое из этих двух слов, и оба они означают одно и то же:
Эта функция называется выводом типов , и многие языки, кроме Scala и C♯, имеют ее: Haskell, Kotlin, Ceylon, ML, F♯, C ++, как вы ее называете. Даже Java имеет ограниченные формы вывода типов.
В динамически типизированных языках программирования переменные даже не имеют типов. Типы существуют только динамически во время выполнения, а не статически. Только значения и выражения имеют типы, и только во время выполнения переменные не имеют типов.
Например, в ECMAScript:
И, наконец, во многих языках вам даже не нужно объявлять переменные вообще. например, в Ruby:
Фактически, этот последний пример действителен для ряда языков программирования. Точно такая же строка кода также будет работать в Python, например.
Так,
источник
auto i = 1 // i is inferred to type int
,vector<int> vec; auto itr = vec.iterator(); // itr is inferred to type vector<int>::iterator
и так далее. Если вы хотите знать, как именно это работает, вы можете посмотреть это в спецификации.Когда вы используете естественный язык для ссылки на информацию, она не очень точна и, в частности, не очень-то сообщает другим о ваших намерениях. Подобные проблемы возникают при попытке сделать математику на естественном языке: она просто недостаточно точна.
Программирование сложное; ошибки слишком легко найти. Типы являются частью системы проверок, которые предназначены для предотвращения недопустимых состояний программы путем обнаружения состояний ошибок. Различные языки используют типы по-разному: некоторые языки интенсивно используют типы для обнаружения ошибок во время компиляции. Почти все языки имеют некоторое представление о несовместимых типах как об ошибке времени выполнения. Обычно ошибка типа указывает на какую-то ошибку в программе. Когда мы позволяем программам продолжать работу, несмотря на ошибки, мы, вероятно, получаем очень плохие ответы. Мы предпочитаем остановить программу, а не получать плохие или неправильные ответы.
Другими словами, типы выражают ограничения на поведение программы. Ограничения, когда применяются каким-либо механизмом, предоставляют гарантии. Такие гарантии ограничивают количество рассуждений, необходимых для размышления о программе, тем самым упрощая задачу чтения и обслуживания программы для программистов. Без типов и их использования инструментов (т.е. компилятора), которые обнаруживают ошибки типов, программная нагрузка значительно выше и, следовательно, более затратна.
Это правда, что (многие) люди легко различают европейские, американские и международные номера телефонов. Однако компьютер на самом деле не «думает» и, если ему скажут, наберет номер телефона США в Европе или наоборот. Например, типы - это хороший способ различать эти случаи, без необходимости учить компьютер «думать». На некоторых языках мы можем получить ошибку времени компиляции при попытке смешать европейский номер телефона в американской телефонной системе. Эта ошибка говорит нам о том, что нам нужно изменить нашу программу (возможно, путем преобразования телефонного номера в международную последовательность набора или вместо использования номера телефона в Европе), прежде чем мы даже попытаемся запустить программу.
Кроме того, поскольку компьютер не думает, имя поля или переменной (например
phonenumber
) ничего не значит для компьютера. Для компьютера это поле / переменная называется просто «blah123». Подумайте, какой была бы ваша программа, если бы все переменные были "blahxxx". Хлоп. Ну, вот что видит компьютер. Предоставление типа дает компьютеру представление о значении переменной, которую он просто не может определить по своему имени.Кроме того, как говорит @Robert, во многих современных языках нам не нужно указывать типы так же часто, как в старые времена, поскольку языки, такие как C #, выполняют «вывод типов», который представляет собой набор правил для определения правильного типа. для переменной в контексте. C # обеспечивает вывод типа только для локальных переменных, но не для формальных параметров или полей класса или экземпляра.
источник
Types are part of a system of checks that ...
поскольку она непосредственно отвечает на OP наWhy do we have to specify data type at all?
..static member add x y = x + y
,member x.Append s = x.Text + s
. В первом случаеx
иy
будет выведеноint
s из-за сложения. Во втором случае они будут такими, какими они являются действительными, в зависимости от типаx.Text
- если это такstring
, тоs
будетstring
также и. Я согласен, что аннотации типов - это документация.В дополнение к другим ответам, есть одна вещь, которая должна быть включена. Помните, что компьютеры - это просто биты. Скажем, я дам вам байты:
Что это значит ? Это хранится таким образом на компьютере, но без какой-либо интерпретации, это просто биты . Это может быть 4 символа ascii. Это может быть целое число. Это может быть несколько байтов в массиве. Это может быть частью объекта. Это может быть указатель на то, где буферизовано это видео. Практически все языки программирования от сборки на вверх необходимости что - то , чтобы знать , как интерпретировать биты , чтобы сделать их делать значимые вычисления.
И поскольку компьютер не может знать значение этих битов, ему нужно, чтобы вы сообщили об этом - либо явно через аннотации типов, либо неявно через механизмы вывода типов, упомянутые в других ответах.
источник
Ответ на вопрос, зачем компьютерам нужна эта информация, связан с представлением данных .
Название «тип данных» является ссылкой на правила, которые помогают компьютеру сохранять и извлекать информацию из его необработанных состояний 0 и 1 в памяти компьютера.
Например, ваш обычный 8-разрядный символ ASCII будет сохранен в памяти компьютера (либо в ОЗУ, либо на диске) как
01000001
(символ «A» в верхнем регистре, код ASCII 65) или00001000
(знак процента), либо как любая комбинация из 0 и 1 в этих 8 битах.Для другого примера некоторое 8-разрядное целое число без знака может быть сохранено как
00000101
(число 5) или00001000
(число 8)Обратите внимание, что двоичное представление числа 8 и символа% может быть одинаковым, но они означают разные вещи, потому что их типы различны.
Даже в языках, которые определяют тип данных, у них может не быть правила о том, что «все типы переменных должны быть объявлены программистом», у них есть правила типа «если ваша серия символов заключена в кавычки, это строка» и еще много правил для каждого типа данных.
Таким образом, даже им нужны типы данных, чтобы понять, что означают 0 и 1, поэтому они могут, например, выполнять функцию конкатенации строк, если вы пытаетесь «добавить» два символа, или делать целочисленное добавление, если вы пытаетесь добавить два целых числа. ,
В вашей истории , скажем, вы не спрашивали Сэма о номере телефона, но Сэм дает вам листок бумаги с надписью «1123581321». Вы не могли быть уверены, что Сэм просто фанат первых восьми чисел Фибоначчи или это номер телефона. Чтобы сделать предположение, вы должны будете принять во внимание контекст и подсказки, которые у вас есть, например, вы, возможно, попросили Сэма назвать номер телефона день назад, или в записке написано «Позвони мне», или если вы посчитаете цифры и найдете это соответствует шаблонам большинства телефонных номеров. Только тогда вы узнаете, что вы можете позвонить по номеру телефона, а не по цифрам, которые вы можете ввести в калькулятор.
Обратите внимание, что эти подсказки, которые привели вас к предположению, что номер был номером телефона, похожи на то, как подсказки приводят компьютерный язык, который не требует объявления для определения типа значения.
источник
В некоторых языках вам не нужно указывать тип данных.
Языки, которые поддерживают вывод типов, обычно могут определить тип данных по вашему использованию. Например,
внутренне типизируется как строка, потому что значение заключено в кавычки.
Некоторые языки не требуют, чтобы вы объявили переменную; переменная создается при первом использовании. Тем не менее, считается, что рекомендуется объявлять переменные по ряду важных причин; в основном потому, что это лучше выражает ваши намерения.
источник
var name = "Ali"
Стиль на самом деле общий для современных статический типизированных языков. В статически типизированных языках тип фиксируется при создании, но он все еще может быть определен инициализатором. Определение языка с динамической типизацией заключается в том, что типы привязываются к значениям, а не к переменным. Присвоение значения переменной, следовательно, также устанавливает тип переменных.var x = 5; x = "";
потому что первый оператор приводитx
к тому, что тип «Number» связан сx
? Вроде конфликты с динамической типизацией . И если нет, то какой эффект имеет тип, связанный с переменной, помимо связи типа со значением?x = "";
изменяет тип x на строковый, даже если это было число ранее.Потому что именно это определяет языковой дизайн. Поэтому, чтобы ответить на ваш вопрос, нам нужно взглянуть на цель явной типизации в таких языках, как C # и C ++. (Ну, C # делает это, потому что C ++ делает это, потому что C делает это, поэтому нам нужно взглянуть на намерение еще тогда).
Во-первых, явная и статическая типизация обеспечивает строгость кодирования - указание переменной как целого означает, что компилятор и программное обеспечение должны быть удивлены и выдавать ошибку, когда вы присваиваете переменную символ или строку. Динамическая типизация может вызвать головную боль у неосторожных (просто взгляните на PHP или подход JavaScript к правдивости таких вещей, как массивы и пустые строки).
Вы можете использовать статические функции с неявной типизацией - инициализация переменной в виде строки означает, что переменная должна быть только строкой, но я чувствую, что это может вызвать проблемы у людей, читающих код (я склонен предполагать динамическую типизацию при неявной типизации ).
Кроме того, в некоторых языках можно написать что-то вроде этого псевдокода для инициализации класса из строкового ввода:
Во-вторых, явная типизация также идет рука об руку с распределением памяти. INT всегда так много байт. PhoneNumber так много байт. Компилятор может назначить блок памяти соответствующего размера, который затем может быть использован позже, без необходимости видеть, сколько места ему понадобится при назначении значения.
Наконец, это устраняет путаницу ... Является ли 123 целым числом или целым числом без знака? Им нужно одинаковое количество байтов, но максимальное значение, хранящееся в переменных любого типа, сильно отличается ...
Это не значит, что явное лучше, чем неявное, но дизайн языка опирается на такие варианты, и C # будет работать по-другому с неявной типизацией. PHP и JavaScript будут работать по-разному с явной типизацией.
источник
Потому что Сэм умнее компиляторов. Например, когда вы говорите, дайте мне номер телефона, вы не указываете, хотите ли вы, чтобы префикс страны или код города был ли это рабочий номер, для которого требуются только последние 4 цифры. Кроме того, если вы спросите номер местной пиццерии, вы сможете ответить на вопрос «pizza4u».
Сэм, это понятно из контекста. Хотя компилятор также может выяснить это из контекста, Сэм будет лучше в этом (и способен прервать процесс, чтобы попросить разъяснений).
Существует два основных подхода к типам и переменным: либо переменная имеет тип, и в этом случае действия, которые не разрешены этим типом, запрещены и препятствуют компиляции, либо значение имеет тип и действия, которые не разрешены типа ловятся во время выполнения.
У каждого подхода есть свои преимущества и недостатки. В общем, авторы компиляторов стараются минимизировать недостатки и максимизировать преимущества. Вот почему C #, например, позволяет
var phoneNumber = GetPhoneNumber();
и будет выводить тип phoneNumber из подписи GetPhoneNumber. Это означает, что вы должны объявить тип для метода, но не переменную, которая получает результат. С другой стороны, для javascript существуют различные проекты хинтинга и принудительного исполнения. Все это компромисс.источник
Это вопрос способа хранения данных. Ваше взаимодействие с Сэмом могло бы быть лучше, если бы вы спросили, чтобы вы могли записать его, но у вас было всего восемь знаков бумаги.
Таким образом, вместо большинства языков вы объявляете тип, поэтому он будет знать и готовиться заранее:
Это становится еще более опасным, когда вы смотрите на фактические основные способы хранения данных. Если вы похожи на меня, у вас есть записная книжка с различными заметками, просто набросанными цифрами, без контекста или ярлыков для чего-либо, и вы не имеете ни малейшего понятия, что это означает через три дня. Это проблема для компьютеров также много раз. Многие языки имеют типы «int» (int, long, short, byte) и «float» (float, double). Почему это необходимо?
Ну, во-первых, давайте посмотрим, как целое число хранится и в целом представлено в компьютере. Вы, вероятно, знаете, что на базовом уровне это все двоичные (1 и 0). Двоичная система - это система счисления, которая работает точно так же, как наша десятичная система счисления. В десятичном исчислении вы считаете от 0 до 9 (с бесконечными подразумеваемыми ведущими нулями, которые вы не пишете), затем вы возвращаетесь к 0 и увеличиваете следующую цифру, чтобы получить 10. Вы повторяете, пока не перевернете с 19 на 20, повторяйте пока не перевернете с 99 на 100 и так далее.
Двоичные значения ничем не отличаются, за исключением того, что вместо 0 до 9 вы считаете от 0 до 1. 0, 1, 10, 11, 100, 101, 110, 111, 1000. Поэтому, когда вы вводите 9, в памяти, записанной в двоичном виде как 1001. Это фактическое число. Он может быть добавлен, вычтен, умножен и т. Д., Именно в этой форме. 10 + 1 = 11. 10 + 10 = 100 (переверните 1 к 0 и держите 1). 11 х 10 = 110 (и, что эквивалентно, 11 + 11 = 110).
Теперь в фактической памяти (включая регистры) есть список, массив, как бы вы ни хотели его назвать, битов (потенциальных 1 или 0 ') рядом друг с другом, что позволяет логически организовывать эти биты для создания число больше 1. Проблема в том, что вы делаете с десятичными числами? Вы не можете просто вставить аппаратную часть между двумя битами в регистре, и будет слишком дорого добавлять «десятичные биты» между каждой парой битов. Так что делать?
Вы кодируете это. Как правило, архитектура процессора или программного обеспечения будет определять, как это делается, но один из распространенных способов - сохранить знак (+ или -, как правило, 1 отрицательный) в первом бите регистра, мантиссе (ваш номер сдвинут однако много раз необходимо избавиться от десятичного числа) для следующего числа битов Х и показателя степени (числа раз, когда вам пришлось его сдвигать) для остатка. Это похоже на научную запись.
Ввод позволяет компилятору знать, на что он смотрит. Представьте, что вы сохранили значение 1,3 в регистре 1. Мы просто придумаем здесь нашу собственную причудливую схему кодирования: 1 бит для знака, 4 для мантиссы, 3 для показателя степени (1 бит для знака, 2 для величины). Это положительное число, поэтому знак положительный (0). Наша мантисса будет 13 (1101), а наш показатель будет -1 (101 (1 для отрицательного, 01 = 1)). Таким образом, мы храним 01101101 в регистре 1. Теперь мы не вводили эту переменную, поэтому, когда среда выполнения начинает использовать ее, она говорит «конечно, это целое число, почему нет», поэтому, когда она печатает значение, мы видим 109 (64 + 32 + 8 + 4 + 1), что явно не правильно.
Однако не каждый язык требует от вас явного ввода. В C # есть ключевое слово «var», которое заставляет интерпретировать тип переменной во время компиляции, а другие языки, такие как Javascript, полностью динамически типизированы, так что вы можете сохранить целое число в переменной, затем присвоить его логическому значению, а затем назначьте это снова строке, и язык отслеживает все это.
Но это намного проще для компилятора, интерпретатора или среды выполнения - и часто приводит к более быстрой программе, поскольку ей не нужно тратить ценные ресурсы на сортировку всего - спросить вас, программиста, какого рода данные вы даете это.
источник
Есть языки программирования, где вам не нужно объявлять типы данных для ваших переменных. Есть даже языки программирования, где вам не нужно объявлять переменные заранее; Вы можете просто использовать их, немедленно.
Проблема с тем, чтобы не объявлять имена переменных, заключается в том, что если вы случайно ошиблись в названии переменной, теперь вы случайно создали новую, совершенно не связанную переменную. Поэтому, когда вы запускаете свою программу, вы не можете понять, почему, черт возьми, эта переменная, которую вы настроили, внезапно ничего не имеет ... Пока, после многих часов отладки, вы не поймете, что набрали проклятое имя неправильно! GRRR !!
Таким образом, они сделали это, поэтому вы должны объявить имена переменных, которые вы собираетесь использовать заранее. И теперь, когда вы вводите имя неправильно, вы получаете ошибку во время компиляции, которая немедленно сообщает вам , где именно находится ошибка, еще до того, как ваша программа даже запустится. Разве это не намного проще?
То же самое касается типов данных. Есть языки программирования, в которых вам не нужно объявлять, какой должен быть тип. Если у вас есть
customer
переменная, которая на самом деле просто имя клиента, а не весь объект клиента, попытка извлечь адрес клиента из простой обычной строки ... не сработает. Весь смысл статической типизации в том, что программа не будет компилироваться; он будет громко жаловаться, указывая на точное место, где проблема. Это гораздо быстрее, чем запускать код и пытаться понять, почему, черт возьми, это не работает.Все это функции, которые сообщают компилятору, что вы намеревались сделать, чтобы он мог проверить, что вы на самом деле сделали, и убедиться, что он имеет смысл. Это позволяет компилятору автоматически находить для вас ошибки, что является большой выгодой.
(В далеком прошлом вам не нужно было объявлять подпрограммы . Вы бы просто
GOSUB
указали номер строки. Если вы хотите передать информацию между подпрограммами, вы бы задали конкретные глобальные переменные, вызвали вашу подпрограмму, а затем проверили другие переменные, когда подпрограмма возвращается. Но это пугающе легко забыть инициализировать один из параметров. Так что теперь почти все современные языки программирования требуют, чтобы вы объявили, какие фактические параметры принимает подпрограмма, поэтому мы можем проверить, что вы указали их все. )источник
var x=1
с похожими результатами. Но это ничего; в Haskell вы можете написать всю свою программу без каких-либо сигнатур типов, но все это статически типизировано, и вы все равно будете получать ошибки, если сделаете ошибку ... (Не совсем мейнстрим, хотя.)for (auto i=0; i<SomeStdVector.size(); ++i)
ваш ЛИНТЕР будет жаловаться , потому что он вывел подписанный тип и вы приступите сравнить его без знака типа. Вы должны написатьauto i=0ul
(поместив информацию о типе в явном виде снова, поэтому следует просто написатьsize_t i=0
в первую очередь).Перейдите в MathOverflow или Теоретическую информатику и некоторое время читайте, чтобы получить представление о том, как люди общались друг с другом, когда хотят убедиться, что нет недопонимания. Или прочитайте стандарт для некоторого зрелого языка программирования.
Вы обнаружите, что определение того, какие значения допустимы для термина, является частью действительно точной практики общения, даже от человека к человеку.
То, что вы заметили, это то, что повседневное общение является довольно регулярным, а человек - довольно отказоустойчивым, поэтому общее понимание участников, как правило, позволяет избежать недоразумений относительно телефонных номеров.
Но вы когда-нибудь пытались снять номер телефона для кого-то в другой стране? Они говорили вам явно, сколько раз нажать ноль, чтобы добраться до международной адресации? Они сказали вам свой код страны? Вы узнали это как таковое? Сколько цифр вы ожидали? Сколько ты получил? Знаете ли вы, как сгруппировать цифры? Или даже если группировка имеет значение?
Внезапно проблема становится намного сложнее, и вы, вероятно, проявили большую осторожность, чтобы явно проверить, что полученное число было понято так, как его имел в виду отправитель.
источник
Другая причина объявления типов - эффективность. Хотя целое число может храниться в 1 байте, или 2 байтах, или 4, программа, использующая очень большое количество переменных, может использовать в 4 раза больше необходимой памяти, в зависимости от того, что делается. Только программист знает, жизнеспособен ли меньший объем памяти, поэтому он может сказать это, объявив тип.
Кроме того, динамически типизированные объекты допускают множество возможных типов на лету. Это может повлечь за собой некоторые накладные расходы "под капотом", замедляя программу по сравнению с залипанием одного типа все время.
источник
На многих ранних языках программирования (особенно на Fortran) не требовалось объявлять переменные перед использованием.
Это привело к ряду проблем. Одним из действительно очевидных является то, что компилятор больше не может отлавливать простые опечатки почти так же надежно. Если у вас есть код, который должен изменить существующую переменную, но содержит опечатку, у вас все еще есть совершенно законный код, который только что создал (и присвоил значение) новую переменную:
Теперь, глядя на это в отдельности, так как я уже упоминал опечатку в качестве источника проблемы, вероятно, довольно легко найти опечатку и проблему здесь. В длинной программе, где это скрыто посреди большого количества другого кода, гораздо проще пропустить.
Даже в настоящее время со многими динамически типизированными языками вы все же можете довольно легко решить ту же самую основную проблему. У некоторых есть возможность предупредить вас, если вы присваиваете переменную, но никогда не читаете ее (которая эвристически отлавливает довольно много подобных проблем), а у других таких вещей нет.
источник
Когда вы объявляете какую-либо переменную, в памяти выделяется некоторое пространство, но компьютер (в данном случае компьютер) еще не знает, сколько места нужно выделить для этой переменной.
Пример: - вы создаете программу, которая просит пользователя ввести любое число, в этом случае вы должны указать тип данных для хранения этого числа, в противном случае машина сама не сможет судить, что она должна выделить 2 байта или 2 гигабайта , если она попытается чтобы выполнить выделение самостоятельно, это может привести к неэффективному использованию памяти. С другой стороны, если вы укажете тип данных в своей программе, то после компиляции машина будет выделять надлежащее пространство в соответствии с необходимостью.
источник