Я прошу в отношении С#, но я предполагаю, что это то же самое на большинстве других языков.
Есть ли у кого-то хорошее определение выражений и утверждений и каковы различия?
Я прошу в отношении С#, но я предполагаю, что это то же самое на большинстве других языков.
Есть ли у кого-то хорошее определение выражений и утверждений и каковы различия?
Выражение: Что-то, что оценивает значение. Пример: 1 + 2/x
Утверждение: Строка кода, которая что-то делает. Пример: GOTO 100
В самых ранних языках программирования общего назначения, таких как FORTRAN, различие было кристально ясным. В FORTRAN оператор был одной единицей исполнения, то, что вы сделали. Единственная причина, по которой его не называли "линией", заключалась в том, что иногда он занимал несколько строк. Выражение само по себе ничего не может сделать... вы должны были присвоить его переменной.
1 + 2 / X
это ошибка в Фортране, потому что она ничего не делает. Вы должны были что-то сделать с этим выражением:
X = 1 + 2 / X
У Фортрана не было грамматики в том виде, в каком мы ее знаем сегодня - эта идея была изобретена вместе с формой Бэкуса-Наура (BNF), как часть определения Алгола-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++, С# и Java), которые все еще имеют некоторые операторы (например, while
), но которые позволяют использовать практически любое выражение в качестве оператора ( в С# в качестве операторов могут использоваться только выражения присваивания, вызова, приращения и декремента; см. ответ скотта Вишневского).
Наличие двух "синтаксических категорий" (так называемых технических терминов для выражений и выражений) может привести к дублированию усилий. Например, C имеет две условные формы, форму заявления
if (E) S1; else S2;
и форма выражения
E ? E1 : E2
А иногда люди хотят дублирования, которого нет: в стандартном C, например, только оператор может объявить новую локальную переменную &— но эта возможность достаточно полезна, чтобы Компилятор GNU C предоставляет расширение GNU, которое позволяет выражению также объявлять локальную переменную.
Проектировщикам других языков не понравился такой тип дублирования, и они рано поняли, что если выражения могут иметь побочные эффекты, а также значения, то синтаксическое различие между утверждениями и выражениями не так уж и полезно - поэтому они избавились от него, Haskell, Icon, Lisp и ML - все языки, которые не имеют синтаксических операторов - они имеют только выражения. Даже структурированные циклы класса и условные формы считаются выражениями, и они имеют значения - но не очень интересные.
Я хотел бы сделать небольшую поправку на ответ Джоэля выше.
С# не позволяет использовать все выражения в качестве операторов. В частности, в качестве операторов могут использоваться выражения присваивания, вызова, приращения и сокращения.
Например, компилятор С# будет указывать следующий код как синтаксическую ошибку:
1 + 2;
Заметим, что в C "=" фактически является оператором, который выполняет две вещи:
Здесь выдержка из грамматики ANSI C. Вы можете видеть, что C не имеет много разных видов операторов... большинство операторов в программе являются операторами выражений, то есть выражением с точкой с запятой в конце.
statement
: labeled_statement
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
;
expression_statement
: ';'
| expression ';'
;
Выражение - это то, что возвращает значение, в то время как оператор не делает.
Примеры:
1 + 2 * 4 * foo.bar() //Expression
foo.voidFunc(1); //Statement
Большая сделка между двумя заключается в том, что вы можете комбинировать выражения вместе, тогда как утверждения не могут быть связаны цепями.
Вы можете найти это на wikipedia, но выражения оцениваются до некоторого значения, в то время как операторы не имеют оцененного значения.
Таким образом, выражения могут использоваться в операторах, но не наоборот.
Обратите внимание, что некоторые языки (такие как Lisp, и я считаю, что Ruby и многие другие) не различают оператор vs expression... на таких языках, все является выражением и может быть привязано к другим выражениям.
Для объяснения важных различий в способности к компоновке (цепочки) выражений против операторов, моя любимая ссылка - статья о награде Джона Бэкуса Тьюринга, Можно ли освободить программирование от стиль фон Неймана?.
Императивные языки (Fortran, C, Java,...) подчеркивают утверждения для структурирования программ и имеют выражения как своего рода последушие. Функциональные языки подчеркивают выражения. Чисто функциональные языки имеют такие мощные выражения, которые могут быть полностью исключены.
Выражения могут быть оценены для получения значения, тогда как операторы не возвращают значение (они имеют тип void).
Выражения вызова функций также могут считаться утверждениями, но если среда выполнения не имеет специальной встроенной переменной, чтобы удерживать возвращаемое значение, то нет способа получить ее.
Ориентированные на инструкцию языки требуют, чтобы все процедуры были списком операторов. Языками, ориентированными на выражения, которые, вероятно, являются все функциональные языки, являются списки выражений или в случае LISP, длинное S-выражение, которое представляет список выражений.
Хотя оба типа могут быть составлены, большинство выражений могут быть составлены произвольно до тех пор, пока типы совпадают. Каждый тип заявления имеет свой собственный способ составления других утверждений, если они могут это сделать. Операторы Foreach и if требуют либо одного статута, либо все подчиненные инструкции идут в блоке оператора один за другим, если только подпункты не разрешают его собственные подстанции.
Заявления также могут включать выражения, в которых выражение действительно не содержит каких-либо утверждений. Тем не менее, одним из исключений было бы выражение лямбда, которое представляет собой функцию, и поэтому может включать в себя все, что может быть включено в функцию, если только язык не разрешает только ограниченные lambdas, такие как lambdas с одним выражением Python.
В языке на основе выражения все, что вам нужно, - это одно выражение для функции, поскольку все структуры управления возвращают значение (многие из них возвращают NIL). Нет необходимости в операторе return, поскольку последнее выражение в функции является возвращаемым значением.
Просто: выражение оценивает значение, утверждение не имеет.
Некоторые вещи, касающиеся языков, основанных на выражениях:
Самое главное: все возвращает значение
Нет разницы между фигурными скобками и фигурными скобками для разграничения кодовых блоков и выражений, поскольку все является выражением. Однако это не мешает лексическому охвату: локальная переменная может быть определена для выражения, в котором содержится его определение, и всех утверждений, содержащихся внутри этого, например.
В языке на основе выражения все возвращает значение. Сначала это может быть немного странно - Что возвращает (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 раз, каждый раз возвращая я в виде строки. Последнее время через возврат 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
)
Оператор представляет собой частный случай выражения, один с типом void
. Тенденция к языку рассматривать заявления по-разному часто вызывает проблемы, и было бы лучше, если бы они были должным образом обобщены.
Например, в С# у нас есть очень полезный 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
? Да. Все, что ему нужно сделать, это потребовать, чтобы за возвратом последовало выражение типа void
. Результат default(void)
будет иметь тип void
, и передаваемый func должен иметь вид Func<TValue, void>
(который был бы эквивалентен Action<TValue>
).
В ряде других ответов подразумевается, что вы не можете связать выражения, подобные выражениям, но я не уверен, откуда эта идея. Мы можем думать о ;
, который появляется после операторов как двоичный инфиксный оператор, беря два выражения типа void
и объединяя их в одно выражение типа void
.
Заявления → Инструкции следовать последовательно
Выражения → Оценка, возвращающая значение
Заявления в основном похожи на шаги или инструкции в алгоритме, результатом выполнения оператора является актуализация указателя инструкции (так называемого в ассемблере)
Выражения не подразумевают и порядок выполнения с первого взгляда, их целью является оценка и возврат значения. В императивных языках программирования оценка выражения имеет порядок, но это только из-за императивной модели, но это не их сущность.
Примеры выражений:
for
goto
return
if
(все они подразумевают продвижение строки (утверждения) выполнения в другую строку)
Пример выражений:
2+2
(это не подразумевает идею исполнения, а оценки)
Заявления являются грамматически полными предложениями. Выражений нет. Например
x = 5
читается как "x получает 5." Это полное предложение. Код
(x + 5)/9.0
читает: "x плюс 5 все разделены на 9.0". Это не полное предложение. Утверждение
while k < 10:
print k
k += 1
является полным предложением. Обратите внимание, что заголовок цикла не является; "в то время как k < 10" является подчиняющим условием.
Оператор - это процедурный строительный блок, из которого создаются все программы на С#. Оператор может объявлять локальную переменную или константу, вызывать метод, создавать объект или присваивать значение переменной, свойству или полю.
Ряд утверждений, окруженных фигурными фигурными скобками, образуют блок кода. Тело метода является одним из примеров кода-кода.
bool IsPositive(int number)
{
if (number > 0)
{
return true;
}
else
{
return false;
}
}
Заявления в С# часто содержат выражения. Выражение в С# является фрагментом кода, содержащим буквальное значение, простое имя или оператор и его операнды.
Выражение - это фрагмент кода, который может быть оценен для одного значения, объекта, метода или пространства имен. Двумя простейшими типами выражений являются литералы и простые имена. Литерал - это постоянное значение, которое не имеет имени.
int i = 5;
string s = "Hello World";
И я и s - простые имена, идентифицирующие локальные переменные. Когда эти переменные используются в выражении, значение переменной извлекается и используется для выражения.
Я предпочитаю значение statement
в формальном логическом смысле слова. Это тот, который изменяет состояние одной или нескольких переменных в вычислении, позволяя сделать истинное или ложное утверждение относительно их значений.
Я предполагаю, что всегда будет путаница в вычислительном мире и науке вообще при введении новой терминологии или слов, существующие слова "перепрофилируются", или пользователи не знают существующей, установленной или "правильной" терминологии для того, описывающая
Я не очень удовлетворен ни одним из ответов здесь. Я посмотрел грамматику на С++ (ISO 2008). Однако, возможно, ради дидактики и программирования ответы могут быть достаточными, чтобы отличить два элемента (реальность выглядит более сложной, хотя).
Оператор состоит из нуля или более выражений, но также может быть и другими языковыми понятиями. Это форма Extended Backus Naur для грамматики (выдержка для утверждения):
statement:
labeled-statement
expression-statement <-- can be zero or more expressions
compound-statement
selection-statement
iteration-statement
jump-statement
declaration-statement
try-block
Мы можем видеть другие понятия, которые рассматриваются в С++.
case
например, это обозначенный операторif
if/else
, case
while
, do...while
, for (...)
break
, continue
, return
(может возвращать выражение), goto
try/catch
blocksЭто выдержка, показывающая часть выражений:
expression:
assignment-expression
expression "," assignment-expression
assignment-expression:
conditional-expression
logical-or-expression assignment-operator initializer-clause
throw-expression
+
, -
, *
, /
, &
, |
, &&
, ||
...)throw
также является выражениемВот итог одного из самых простых ответов, которые я нашел.
Первоначально Ответал Anders Kaseorg
Оператор представляет собой полную строку кода, которая выполняет какое-либо действие, а выражение - это любой раздел кода, который оценивает значение.
Выражения могут быть объединены "горизонтально" в более крупные выражения с использованием операторов, тогда как утверждения могут объединяться только "по вертикали", записывая один за другим или с блочными конструкциями.
Каждое выражение может использоваться как оператор (эффект которого заключается в оценке выражения и игнорировании результирующего значения), но большинство операторов не могут использоваться как выражения.
Фактическая основа этих концепций:
Выражения: синтаксическая категория, экземпляр которой можно оценить как значение.
Утверждение: синтаксическая категория, экземпляр которой может быть связан с оценками выражения и результирующее значение оценки (если есть) не гарантировано доступно.
Помимо самого начального контекста ФОРТРАНА в первые десятилетия, оба определения выражений и утверждений в принятом ответе явно неверны:
sizeof
, никогда не вычисляется.(Кстати, я хочу добавить [цитата нужна] к этому ответу относительно материалов о C, потому что я не могу вспомнить, есть ли у DMR такие мнения. Кажется, нет, в противном случае не должно быть никаких причин сохранять дублирование функциональности в дизайне C: в частности, оператор запятой против операторов.)
(Следующее обоснование не является прямым ответом на первоначальный вопрос, но я считаю необходимым уточнить кое-что, на что здесь уже дан ответ.)
Тем не менее, сомнительно, что нам нужна особая категория "операторов" в языках программирования общего назначения:
begin
на схеме) или синтаксическим сахаром монадных структур.++i + ++i
не имеет смысла в C.)Так почему заявления? Во всяком случае, история уже беспорядок. Кажется, что большинство языковых дизайнеров не принимают свой выбор тщательно.
Хуже того, он даже дает некоторым энтузиастам систем типов (которые недостаточно знакомы с историей PL) некоторые заблуждения о том, что системы типов должны иметь важные вещи, связанные с более существенными разработками правил операционной семантики.
Серьезно, рассуждение в зависимости от типов не так уж плохо во многих случаях, но особенно неконструктивно в этом особом. Даже эксперты могут все испортить.
Например, кто-то подчеркивает, что типизирующий характер является главным аргументом против традиционного подхода к неограниченным продолжениям. Хотя этот вывод несколько обоснован, и понимание составных функций в порядке (, но все же слишком наивно для сути), этот аргумент не является обоснованным, поскольку на практике он полностью игнорирует подход "побочного канала", такой как _Noreturn any_of_returnable_types
(в C11) для кодирования Falsum
. И, строго говоря, абстрактная машина с непредсказуемым состоянием не идентична "разбитому компьютеру".
Чтобы улучшить и подтвердить мой предыдущий ответ, определения терминов языка программирования должны быть объяснены, если это применимо, в теории типов компьютерных наук.
Выражение имеет тип, отличный от типа Bottom, т.е. имеет значение. Оператор имеет тип Unit или Bottom.
Из этого следует, что оператор может иметь какой-либо эффект только в программе, когда он создает побочный эффект, поскольку он либо не может вернуть значение, либо возвращает только значение типа Unit, которое либо не назначается (в некоторые языки, такие как C void
) или (например, в Scala), могут храниться для отложенной оценки оператора.
Очевидно, что a @pragma
или /*comment*/
не имеют типа и, следовательно, отличаются от операторов. Таким образом, единственным типом утверждения, которое не было бы побочных эффектов, было бы нерабочее. Неиспользование полезно только в качестве заполнителя для будущих побочных эффектов. Любые другие действия, связанные с выражением, будут побочным эффектом. Снова подсказка компилятора, например. @pragma
, не является утверждением, потому что он не имеет типа.
В точности, оператор должен иметь "побочный эффект" (т.е. обязательно), а выражение должно иметь значение (т.е. не нижний тип).
тип оператора - это тип единицы измерения, но из-за модуля теоремы Halting это вымысел, поэтому скажем нижний тип.
Void
не является точно нижним типом (это не подтип всех возможных типов). Он существует на языках, которые не имеют полностью звуковой системы типа. Это может показаться снобистским выражением, но полнота такая как аннотации вариаций, имеет решающее значение для написания расширяемого программного обеспечения.
Посмотрите, что Wikipedia может сказать по этому вопросу.
https://en.wikipedia.org/wiki/Statement_(computer_science)
В компьютерном программировании оператор является наименьшим автономным элементом языка императивного, который выражает какое-либо действие.
Многие языки (например, C) делают различие между операторами и определениями, причем оператор содержит только исполняемый код и определение, объявляющее идентификатор, в то время как выражение оценивает только значение.