Выражение: Что-то, что оценивает значение. Пример: 1 + 2 / x
Заявление: Строка кода, которая что-то делает. Пример: GOTO 100
В самых ранних языках программирования общего назначения, таких как FORTRAN, различие было кристально ясным. В FORTRAN оператор был одной единицей исполнения, то, что вы сделали. Единственная причина, по которой его не называли «линией», заключалась в том, что иногда он занимал несколько строк. Выражение само по себе ничего не может сделать ... Вы должны были присвоить его переменной.
1 + 2 / X
- ошибка в Фортране, потому что она ничего не делает. Вы должны были что-то сделать с этим выражением:
X = 1 + 2 / X
У Фортрана не было грамматики в том виде, в каком мы ее знаем сегодня - эта идея была изобретена вместе с формой Бэкуса-Наура (БНФ), как часть определения Алгола-60. В этот момент семантическое различие («имейте значение» и «делайте что-то») было закреплено в синтаксисе : один вид фразы был выражением, а другой - выражением, а парсер может отличить их друг от друга.
Разработчики более поздних языков размыли различие: они позволяли синтаксическим выражениям делать что-то, и они допускали синтаксические выражения, которые имели значения.
Самый ранний популярный пример языка, который до сих пор сохранился, - C. Разработчики C поняли, что никакого вреда не было, если вам позволили оценить выражение и выбросить результат. В C каждое синтаксическое выражение может быть превращено в утверждение, просто ставя точку с запятой в конце:
1 + 2 / x;
- абсолютно законное утверждение, хотя абсолютно ничего не произойдет. Аналогично, в C выражение может иметь побочных эффектов - оно может что-то изменить.
1 + 2 / callfunc(12);
потому что callfunc
может просто сделать что-то полезное.
Как только вы позволите любому выражению быть оператором, вы также можете разрешить оператор присваивания (=) внутри выражений. Вот почему C позволяет вам делать такие вещи, как
callfunc(x = 2);
Это вычисляет выражение x = 2 (присваивая значение от 2 до x) и затем передает это (2) в функцию callfunc
.
Это размывание выражений и операторов происходит во всех C-производных (C, C ++, C # и Java), которые все еще имеют некоторые операторы (например, while
), но позволяют использовать почти любое выражение в качестве оператора. (в C # в качестве операторов могут использоваться только выражения присваивания, вызова, приращения и декремента; см. ответ Скотта Вишневского ).
Наличие двух «синтаксических категорий» (это техническое название для того, что такое выражение и выражение) может привести к дублированию усилий. Например, C имеет две условные формы, форму заявления
if (E) S1; else S2;
и форма выражения
E ? E1 : E2
А иногда люди хотят дублирования, которого нет: в стандартном C, например, только оператор может объявить новую локальную переменную & mdash; но эта способность достаточно полезна, чтобы
Компилятор GNU C предоставляет расширение GNU, которое позволяет выражению также объявлять локальную переменную.
Разработчикам других языков не понравился такой тип дублирования, и они рано поняли, что если выражения могут иметь побочные эффекты, а также значения, то синтаксическое различие между утверждениями и выражениями - не все это полезно - так что они избавились от этого. Haskell, Icon, Lisp и ML - все языки, которые не имеют синтаксических операторов - они имеют только выражения. Даже структурированные циклы класса и условные формы считаются выражениями, и они имеют значения - но не очень интересные.