Почему многие языки программирования ставят тип * после * имя переменной?

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

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

var : int

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

Ответы

Ответ 1

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

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

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

Синтаксис

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

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

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

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

Ответ 2

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

type last читает как "создать переменную с именем NAME типа TYPE"

это, конечно, наоборот, говоря: "Создайте TYPE под именем NAME", но когда вы думаете об этом, значение, которое имеет значение, более важно, чем тип, тип является просто программным ограничением на данные

Ответ 3

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

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

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

Ответ 4

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

Сравнение

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

и

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

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

Ответ 5

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

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

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

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

Ответ 6

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

Помещение типа до того, как переменная запущена достаточно беззаботно с помощью Fortran и Algol, но на C она стала действительно уродливой, где некоторые переменные типа применяются перед переменной, а другие после. Вот почему в C у вас есть такие красоты, как

int (*p)[10];

или

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

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

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

p: pointer to array[10] of int

Контраст с

q: array[10] of pointer to int

который в C,

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

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

Ответ 7

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

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

Ответ 8

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

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

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

Ответ 9

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

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

Ответ 10

Включение метода first помогает при синтаксическом анализе. Например, в C, если вы объявили переменные типа

x int;

Когда вы разбираете только x, вы не знаете, является ли x объявлением или выражением. Напротив, при

int x;

Когда вы разбираете int, вы знаете, что находитесь в объявлении (типы всегда запускают какое-либо объявление).

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

Ответ 11

Фортран сначала ставит тип:

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

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

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

Ответ 12

Как насчет динамического (приветствия @wcoenen) типизированных языков? Вы просто используете переменную.