Почему многие языки программирования ставят тип * после * имени переменной? - PullRequest
15 голосов
/ 11 ноября 2009

Я только что натолкнулся на этот вопрос в FAQ по Go, и он напомнил мне кое-что, что меня давно беспокоило. К сожалению, я не очень понимаю, к чему идет ответ.

Кажется, что почти каждый не C-подобный язык ставит тип после имени переменной, например:

var : int

Просто из чистого любопытства, почему это? Есть ли преимущества выбора одного или другого?

Ответы [ 12 ]

12 голосов
/ 11 ноября 2009

Есть проблема с синтаксическим анализом, как говорит Кит Рэндалл, но это не то, что он описывает. «Не знать, является ли это декларацией или выражением», просто не имеет значения - вас не волнует, является ли это выражением или декларацией, пока вы все равно не проанализируете все это, после чего неоднозначность разрешится.

Используя синтаксический анализатор без контекста, не имеет значения, идет ли тип до или после имени переменной. Важно то, что вам не нужно искать определяемые пользователем имена типов, чтобы понять спецификацию типов - вам не нужно понимать все, что было до того, чтобы понять текущий токен.

Синтаксис Паскаля не зависит от контекста - если не полностью, по крайней мере, WRT эта проблема. Тот факт, что имя переменной стоит на первом месте, менее важен, чем такие детали, как разделитель двоеточий и синтаксис описания типов.

Синтаксис C зависит от контекста. Чтобы синтаксический анализатор мог определить, где заканчивается описание типа и какой токен является именем переменной, он должен уже интерпретировать все предшествующее, чтобы он мог определить, является ли данный токен идентификатора именем переменной или просто другим токеном, способствующим описание типа.

Поскольку синтаксис C является контекстно-зависимым, его очень сложно (если не невозможно) анализировать с использованием традиционных инструментов генератора синтаксических анализаторов, таких как yacc / bison, тогда как синтаксис Pascal легко анализировать с использованием тех же инструментов. Тем не менее, теперь есть генераторы парсеров, которые могут справиться с синтаксисом C и даже C ++. Хотя это не правильно задокументировано или в 1.? выпуск и т. д., мой личный фаворит - Kelbt , который использует возвратный LR и поддерживает семантическое "отмену" - в основном, отменяет добавления в таблицу символов, когда спекулятивный анализ оказывается неправильным.

На практике парсеры C и C ++ обычно написаны от руки, смешивая рекурсивный спуск и разбор приоритетов. Я предполагаю, что то же самое относится к Java и C #.

Кстати, похожие проблемы с контекстной чувствительностью при разборе C ++ создали много неприятностей. « Альтернативный синтаксис функции » для C ++ 0x обходит аналогичную проблему, перемещая спецификацию типа в конец и помещая ее после разделителя - очень похоже на двоеточие Pascal для возвращаемых функций функций. Это не избавляет от чувствительности к контексту, но принятие подобного Паскалю соглашения делает его немного более управляемым.

11 голосов
/ 11 ноября 2009

«большинство других» языков, о которых вы говорите, являются более декларативными.Они нацелены на то, чтобы вы могли программировать больше в соответствии с тем, о чем вы думаете (при условии, что вы не ограничены императивным мышлением).

введите последнее чтение как «создайте переменную с именем NAME типа TYPE»

это, конечно, противоположно высказыванию «создайте ТИП с именем NAME», но когда вы думаете об этом, значение для него более важно, чем тип, тип является просто программным ограничением на данные

8 голосов
/ 11 ноября 2009

Тенденция к увеличению заключается в том, чтобы вообще не указывать тип или опционально указывать тип. Это может быть динамически типизированный язык, в котором на самом деле нет типа переменной, или это может быть статически типизированный язык, который выводит тип из контекста.

Если тип иногда задается, а иногда выводится, то легче прочитать, если после этого появляется дополнительный бит.

Существуют также тенденции, связанные с тем, считает ли язык себя пришедшим из школы C или функциональной школы, или что-то еще, но это пустая трата времени. Языки, которые улучшают свои предшественники и заслуживают изучения, - это те, которые готовы принимать вклады из разных школ на основе заслуг, а не придирчивы к наследию функции.

8 голосов
/ 11 ноября 2009

Если имя переменной начинается со столбца 0, проще найти имя переменной.

Сравнить

QHash<QString, QPair<int, QString> > hash;

и

hash : QHash<QString, QPair<int, QString> >;

Теперь представьте, насколько более читабельным может быть ваш типичный заголовок C ++.

6 голосов
/ 11 ноября 2009

В теории формального языка и теории типов это почти всегда пишется как var: type. Например, в набранном лямбда-исчислении вы увидите доказательства, содержащие такие утверждения, как:

x : A   y : B
-------------
 \x.y : A->B

Я не думаю, что это действительно имеет значение, но я думаю, что есть два оправдания: одно состоит в том, что «x: A» читается как «x имеет тип A», другое - что тип подобен набору int - это набор целых чисел), а обозначение относится к "x ε A".

Некоторые из этих вещей предшествуют современным языкам, о которых вы думаете.

5 голосов
/ 24 декабря 2010

«Те, кто не может вспомнить прошлое, обречены повторять его».

Помещение типа до того, как переменная начиналась достаточно безобидно с Фортрана и Алгола, но это стало ужасно в Си, где некоторые модификаторы типов применяются до переменной, другие после. Вот почему в C у вас есть такие красавицы, как

int (*p)[10];

или

void (*signal(int x, void (*f)(int)))(int)

вместе с утилитой (cdecl), целью которой является расшифровка такого бреда.

В Паскале тип идет после переменной, поэтому первые примеры становятся

p: pointer to array[10] of int

Контрастность с

q: array[10] of pointer to int

который в С равен

int *q[10]

В C вам нужны круглые скобки, чтобы отличить это от int (* p) [10]. Скобки не требуются в Паскале, где важен только порядок.

Функция сигнала будет

signal: function(x: int, f: function(int) to void) to (function(int) to void)

Все еще глоток, но, по крайней мере, в сфере человеческого понимания.

Честно говоря, проблема не в том, что C ставит типы перед именем, а в том, что он извращенно настаивает на том, чтобы ставить кусочки до, а другие после имени.

Но если вы попытаетесь поставить все перед именем, порядок все равно не интуитивен:

int [10] a // an int, ahem, ten of them, called a
int [10]* a // an int, no wait, ten, actually a pointer thereto, called a

Итак, ответ таков: разумно разработанный язык программирования ставит переменные перед типами, потому что результат более читабелен для людей.

1 голос
/ 16 ноября 2009

Я всегда думал, что способ C делает это немного странным: вместо конструирования типов пользователь должен объявлять их неявно. Это не только до / после имени переменной; в общем случае вам может потребоваться встроить имя переменной в атрибуты типа (или, в некоторых случаях, встроить пустое пространство, где имя будет , если вы фактически его объявляете).

Как слабая форма сопоставления с образцом, она в некоторой степени понятна, но, похоже, также не дает особых преимуществ. И попытка написать (или прочитать) тип указателя на функцию может легко вывести вас за пределы готовности к пониманию. В общем, этот аспект C является недостатком, и я рад видеть, что Go оставил его позади.

1 голос
/ 11 ноября 2009

Я не уверен, но я думаю, что это связано с концепцией «имя против существительного».

По сути, если вы ставите тип первым (например, «int varname»), вы объявляете «целое число с именем« varname »»; то есть вы даете экземпляру типа имя. Однако, если вы сначала указали имя, а затем тип (например, «varname: int»), вы скажете: «это« varname »; это целое число». В первом случае вы даете экземпляру чего-то имя; во втором вы определяете существительное и заявляете, что это экземпляр чего-то.

Это немного похоже на то, если бы вы определяли стол как предмет мебели; выражение «это мебель, и я называю это« стол »» (напечатайте первым) отличается от выражения «стол - это вид мебели» (напечатайте последнее).

1 голос
/ 11 ноября 2009

Это просто, как язык был разработан. Visual Basic всегда был таким.

Большинство (если не все) языки фигурных скобок ставят тип первым. Для меня это более интуитивно понятно, поскольку та же позиция также указывает тип возвращаемого значения метода. Таким образом, входные данные заключаются в круглые скобки, а выходные - в конце имени метода.

0 голосов
/ 11 ноября 2009

Fortran ставит тип первым:

REAL*4 I,J,K
INTEGER*4 A,B,C

И да, есть (очень слабая) шутка для тех, кто знаком с Фортраном.

Можно утверждать, что это проще, чем C, который помещает информацию о типе вокруг имени, когда тип достаточно сложен (например, указатели на функции).

...