Выражение против утверждения - PullRequest
420 голосов
/ 21 августа 2008

Я спрашиваю относительно c #, но я полагаю, что то же самое в большинстве других языков.

Есть ли у кого-нибудь хорошее определение выражений и выражений и в чем различия?

Ответы [ 18 ]

508 голосов
/ 21 августа 2008

Выражение: Что-то, что оценивает значение. Пример: 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 - все языки, которые не имеют синтаксических операторов - они имеют только выражения. Даже структурированные циклы класса и условные формы считаются выражениями, и они имеют значения - но не очень интересные.

53 голосов
/ 21 августа 2008

Я бы хотел внести небольшую поправку в ответ Джоэла выше.

C # не позволяет использовать все выражения в качестве операторов. В частности, в качестве операторов могут использоваться только выражения присваивания, вызова, приращения и уменьшения.

Например, компилятор C # пометит следующий код как синтаксическую ошибку:

1 + 2;

21 голосов
/ 21 августа 2008
  • выражение - это все, что дает значение: 2 + 2
  • оператор является одним из основных «блоков» выполнения программы.

Обратите внимание, что в C "=" на самом деле является оператором, который делает две вещи:

  • возвращает значение правого подвыражения.
  • копирует значение правого подвыражения в переменную слева.

Вот выдержка из грамматики ANSI C. Вы можете видеть, что C не имеет много разных видов операторов ... большинство операторов в программе - это операторы выражений, то есть выражения с точкой с запятой в конце.

statement
    : labeled_statement
    | compound_statement
    | expression_statement
    | selection_statement
    | iteration_statement
    | jump_statement
    ;

expression_statement
    : ';'
    | expression ';'
    ;

http://www.lysator.liu.se/c/ANSI-C-grammar-y.html

14 голосов
/ 21 августа 2008

Выражение - это то, что возвращает значение, а выражение - нет.

Например:

1 + 2 * 4 * foo.bar()     //Expression
foo.voidFunc(1);          //Statement

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

8 голосов
/ 21 августа 2008

Вы можете найти это в википедии , но выражения оцениваются до некоторого значения, в то время как операторы не имеют оцененного значения.

Таким образом, выражения могут использоваться в выражениях, но не наоборот.

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

7 голосов
/ 24 июня 2009

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

В императивных языках (Fortran, C, Java и т. Д.) Подчеркиваются операторы для структурирования программ, а выражения - это своего рода запоздалая мысль. Функциональные языки подчеркивают выражения. Чисто функциональные языки имеют такие мощные выражения, что операторы можно полностью исключить.

5 голосов
/ 21 августа 2008

Проще: выражение оценивается как значение, а выражение - нет.

5 голосов
/ 21 августа 2008

Выражения могут быть оценены для получения значения, тогда как операторы не возвращают значение (они имеют тип void ).

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

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

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

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

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

3 голосов
/ 19 декабря 2008

Некоторые сведения о языках, основанных на выражениях:


Самое важное: все возвращает значение


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


На языке выражений все возвращает значение. Поначалу это может показаться немного странным - что возвращает (FOR i = 1 TO 10 DO (print i))?

Несколько простых примеров:

  • (1) возвращает 1
  • (1 + 1) возврат 2
  • (1 == 1) возврат TRUE
  • (1 == 2) возврат FALSE
  • (IF 1 == 1 THEN 10 ELSE 5) возвращает 10
  • (IF 1 == 2 THEN 10 ELSE 5) возвращает 5

Пара более сложных примеров:

  • Некоторые вещи, такие как вызовы некоторых функций, на самом деле не имеют значимого значения, которое нужно возвращать (Вещи, которые вызывают только побочные эффекты?). Вызов OpenADoor(), FlushTheToilet() или TwiddleYourThumbs() вернет какое-то обычное значение, например OK, Done или Success.
  • Когда несколько несвязанных выражений вычисляются в одном большем выражении, значение последней вещи, вычисленной в большом выражении, становится значением большого выражения. Чтобы взять пример (FOR i = 1 TO 10 DO (print i)), значение цикла for равно «10», это заставляет выражение (print i) вычисляться 10 раз, каждый раз возвращая i в виде строки. В последний раз возвращается 10, наш окончательный ответ

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

В качестве быстрого примера:

 FOR i = 1 to (IF MyString == "Hello, World!" THEN 10 ELSE 5) DO
 (
    LotsOfCode
 )

является вполне допустимой заменой не основанного на выражениях

IF MyString == "Hello, World!" THEN TempVar = 10 ELSE TempVar = 5 
FOR i = 1 TO TempVar DO
(    
    LotsOfCode  
)

В некоторых случаях макет, который позволяет код на основе выражений, кажется мне более естественным

Конечно, это может привести к безумию. В рамках хобби-проекта на языке сценариев на основе выражений под названием MaxScript мне удалось придумать эту линию монстров

IF FindSectionStart "rigidifiers" != 0 THEN FOR i = 1 TO (local rigidifier_array = (FOR i = (local NodeStart = FindsectionStart "rigidifiers" + 1) TO (FindSectionEnd(NodeStart) - 1) collect full_array[i])).count DO
(
    LotsOfCode
)
3 голосов
/ 14 марта 2009

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

Например, в C # у нас есть очень полезный Func<T1, T2, T3, TResult> перегруженный набор общих делегатов. Но мы также должны иметь соответствующий набор Action<T1, T2, T3>, и программирование более высокого порядка общего назначения постоянно должно дублироваться, чтобы справиться с этой неудачной бифуркацией.

Простой пример - функция, которая проверяет, является ли ссылка нулевой, перед вызовом другой функции:

TResult IfNotNull<TValue, TResult>(TValue value, Func<TValue, TResult> func)
                  where TValue : class
{
    return (value == null) ? default(TValue) : func(value);
}

Может ли компилятор иметь дело с возможностью TResult быть void? Да. Все, что ему нужно сделать, это потребовать, чтобы за return следовало выражение типа void. Результат default(void) будет иметь тип void, а передаваемый функционал должен иметь форму Func<TValue, void> (что будет эквивалентно Action<TValue>).

Ряд других ответов подразумевает, что вы не можете связывать утверждения, как вы, с выражениями, но я не уверен, откуда взялась эта идея. Мы можем думать о ;, который появляется после операторов, как двоичный инфиксный оператор, беря два выражения типа void и объединяя их в одно выражение типа void.

...