表达与陈述

时间:2008-08-21 00:28:00

标签: c# language-features expression

我问的是关于c#,但我认为它在大多数其他语言中都是一样的。

有没有人对表达式语句有一个很好的定义,有什么区别?

22 个答案:

答案 0 :(得分:508)

表达式:评估为值的内容。示例: 1 + 2 / x
声明:执行某项操作的代码行。示例: GOTO 100

在最早的通用编程语言中,如FORTRAN,区别非常清晰。在FORTRAN中,声明是一个执行单元,你做了一件事。它不被称为“线”的唯一原因是因为有时它跨越多条线。表达式本身无法做任何事情......你必须将它分配给一个变量。

1 + 2 / X

是FORTRAN中的错误,因为它不执行任何操作。你必须对那个表达做一些事情:

X = 1 + 2 / X

FORTRAN没有我们今天所知的语法 - 这个想法与Backus-Naur Form(BNF)一起被发明,作为Algol-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#中只有赋值,调用,递增和递减表达式可以用作语句;参见Scott Wisniewski's answer)。

有两个“语法类别”(这是事物语句和表达式的技术名称)会导致重复工作。例如,C有两种形式的条件,声明形式

if (E) S1; else S2;

和表达形式

E ? E1 : E2

有时人们想要不存在的重复:例如,在标准C中,只有一个语句可以声明一个新的局部变量 - 但是这个能力足够有用了 GNU C编译器提供了一个GNU扩展,它使表达式也能声明一个局部变量。

其他语言的设计者不喜欢这种重复,他们很早就看到如果表达式既有副作用也有值,那么语句和表达式之间的语法区别不是所有有用的 - 所以他们摆脱它。 Haskell,Icon,Lisp和ML都是没有语法语句的语言 - 它们只有表达式。即使是类结构化循环和条件形式也被认为是表达式,它们具有值 - 但不是非常有趣的。

答案 1 :(得分:53)

我想对乔尔的答案做一个小小的修正。

C#不允许将所有表达式用作语句。特别是,只有赋值,调用,递增和递减表达式可以用作语句。

例如,C#编译器会将以下代码标记为语法错误:

1 + 2;

答案 2 :(得分:21)

  • 表达式是任何产生值的东西: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

答案 3 :(得分:14)

表达式返回值,而语句则不返回。

例如:

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

两者之间的大问题是你可以将表达式链接在一起,而语句不能被链接。

答案 4 :(得分:8)

您可以在wikipedia上找到此信息,但表达式会被评估为某个值,而语句则没有评估值。

因此,表达式可以用在语句中,但不能用于其他方式。

请注意,某些语言(例如Lisp,我相信Ruby和许多其他语言)不区分语句与表达式......在这些语言中,一切都是表达式,可以与其他表达式链接。

答案 5 :(得分:7)

对于表达式与语句的可组合性(可链接性)的重要差异的解释,我最喜欢的参考是John Backus的图灵奖论文 Can programming be liberated from the von Neumann style?

命令式语言(Fortran,C,Java,...)强调结构化程序的语句,并将表达式作为一种思考后的方式。功能语言强调表达。 纯粹的函数式语言具有如此强大的表达式,而不是语句可以完全消除。

答案 6 :(得分:5)

简单地说:表达式求值为值,语句不求值。

答案 7 :(得分:5)

可以计算表达式以获取值,而语句不返回值(它们的类型为 void )。

函数调用表达式当然也可以被认为是语句,但除非执行环境有一个特殊的内置变量来保存返回的值,否则无法检索它。

面向语句的语言要求所有过程都是语句列表。面向表达的语言,可能是所有函数式语言,是表达式列表,或者在LISP的情况下,是表示表达式列表的一个长S表达式。

虽然可以组合两种类型,但只要类型匹配,大多数表达式都可以任意组合。每种类型的语句都有自己的组合其他语句的方式,如果他们可以做到这一切。 Foreach和if语句要求单个语句或所有从属语句一个接一个地进入语句块,除非子语句允许其自己的子语句。

语句还可以包含表达式,其中表达式实际上不包含任何语句。但是,一个例外是lambda表达式,它表示一个函数,因此可以包含函数可以包含的任何内容,除非该语言仅允许有限的lambdas,如Python的单表达式lambda。

在基于表达式的语言中,您需要的只是函数的单个表达式,因为所有控制结构都返回一个值(其中很多都返回NIL)。由于函数中最后一次计算的表达式是返回值,因此不需要返回语句。

答案 8 :(得分:3)

关于基于表达的语言的一些事情:


最重要的是:一切都会返回值


用于分隔代码块和表达式的花括号和大括号之间没有区别,因为所有内容都是表达式。这不会阻止词法作用域:例如,可以为包含其定义的表达式以及包含在其中的所有语句定义局部变量。


在基于表达式的语言中,一切都返回一个值。起初这可能有点奇怪 - (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()会返回某种世俗价值,例如“确定”,“完成”或“成功”。
  • 当在一个较大的表达式中计算多个未链接的表达式时,在大表达式中计算的最后一个事物的值将成为大表达式的值。以(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
)

答案 9 :(得分:3)

语句是表达式的特例,具有void类型的表达式。语言以不同方式处理语句的倾向经常会导致问题,如果它们被适当地推广会更好。

例如,在C#中,我们有一组非常有用的Func<T1, T2, T3, TResult>重载泛型委托。但是我们也必须有一个相应的Action<T1, T2, T3>集,并且必须不断复制通用的高阶编程来处理这种不幸的分歧。

简单示例 - 在调用另一个函数之前检查引用是否为null的函数:

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>)。< / p>

其他一些答案暗示你不能用表达式链接语句,但我不确定这个想法来自何处。我们可以将语句后面出现的;视为二进制中缀运算符,采用类型为void的两个表达式,并将它们组合成void类型的单个表达式。

答案 10 :(得分:2)

陈述 - &gt;按顺序说明的说明
表达式 - &gt;返回值的评估

语句基本上类似于步骤或算法中的指令,执行语句的结果是指令指针的实现(所谓的汇编程序)

表达式并不意味着第一眼就看到执行顺序,它们的目的是评估并返回一个值。在命令式编程语言中,表达式的评估有一个顺序,但它只是因为命令式模型,但它不是它们的本质。

陈述的例子:

for
goto
return
if

(所有这些都意味着将执行的行(声明)推进到另一行)

表达式示例:

2+2

(它并不意味着执行的想法,而是评估的意思)

答案 11 :(得分:1)

语句是语法上完整的句子。表达式不是。例如

x = 5

读为“x得到5”这是一个完整的句子。代码

(x + 5)/9.0

读取,“x加5除以9.0。”这不是一个完整的句子。声明

while k < 10: 
    print k
    k += 1

是一个完整的句子。请注意,循环头不是; “而k <10,”是一个从属条款。

答案 12 :(得分:1)

<强> Statement

语句是一个程序构建块,构建所有C#程序。语句可以声明局部变量或常量,调用方法,创建对象或为变量,属性或字段赋值。

由大括号括起来的一系列语句构成了一个代码块。方法体是代码块的一个例子。

bool IsPositive(int number)
{
    if (number > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

C#中的语句通常包含表达式。 C#中的表达式是包含文字值,简单名称或运算符及其操作数的代码片段。

<强> Expression

表达式是可以计算为单个值,对象,方法或命名空间的代码片段。两种最简单的表达式是文字和简单名称。文字是一个没有名字的常数值。

int i = 5;
string s = "Hello World";

i和s都是识别局部变量的简单名称。当在表达式中使用这些变量时,将检索变量的值并将其用于表达式。

答案 13 :(得分:1)

我更喜欢statement在形式逻辑意义上的含义。它是一个改变计算中一个或多个变量状态的函数,可以对它们的值进行真或假的陈述。

我想计算世界和科学总会在引入新的术语或词语时出现混乱,现有的词语被“改变用途”或者用户不了解现有的,既定的或“适当的”术语。描述

答案 14 :(得分:1)

我对这里的任何答案都不满意。我看了C++ (ISO 2008)的语法。然而,也许为了教学和编程,答案可能足以区分这两个元素(虽然现实看起来更复杂)。

语句由零个或多个表达式组成,但也可以是其他语言概念。这是语法的扩展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

我们可以在C ++中看到被认为是语句的其他概念。

  • 表达式语句是自我解释的(语句可以包含零或更多表达式,仔细阅读语法,这很棘手)
  • case例如是标签声明
  • selection-statement if if/elsecase
  • iteration-statement whiledo...whilefor (...)
  • 跳转语句breakcontinuereturn(可以返回表达式),goto
  • declaration-statement 是一组声明
  • try-block 是表示try/catch
  • 的语句
  • 并且可能还有更多的语法

这是一个显示表达式部分的摘录:

expression:
        assignment-expression
        expression "," assignment-expression
assignment-expression:
        conditional-expression
        logical-or-expression assignment-operator initializer-clause
        throw-expression
  • 表达式经常包含或分配
  • 条件表达式(听起来有误导性)是指运算符的使用(+-*/,{{ 1}},&|&&,...)
  • throw-expression - 呃? ||子句也是一个表达式

答案 15 :(得分:0)

Expression

一种可以评估为一定值的语法。换句话说,表达式是表达式元素(如文字,名称,属性访问,运算符或函数调用)的累积,它们都返回一个值。与许多其他语言相反,并非所有语言构造都是表达式。还有一些语句不能用作表达式,例如while。分配也是语句,而不是表达式。

Statement

语句是套件(代码的“块”)的一部分。语句可以是表达式,也可以是带有关键字的多个结构之一,例如if,while或for。

答案 16 :(得分:0)

语句是不返回任何内容的代码块,它只是一个独立的执行单元。例如-

 if(a>=0)
    return a+10;//This is an expression because it evalutes an new value;

另一方面,表达式返回或评估新值。例如-

 a=10+y;//This is also an expression because it returns a new value. 

foo

答案 17 :(得分:0)

在面向语句的编程语言中,代码块被定义为语句列表。换句话说,语句是一种语法,您可以将其放入代码块中而不会引起语法错误。

维基百科类似地定义了单词陈述

在计算机编程中,语句是命令式编程语言的语法单元,表示要执行的某些操作。用这种语言编写的程序是由一个或多个语句的序列组成的

请注意后一种说法。 (尽管在这种情况下“程序”在技术上是错误的,因为C和Java都拒绝一个不包含任何语句的程序。)

维基百科将单词表达式定义为

编程语言中的表达式是一种语法实体,可以对其进行评估以确定其值

但是,这是错误的,因为在Kotlin中,throw new Exception("")是一个表达式,但是在被求值时,它只是抛出异常,而从不返回任何值。

在静态类型的编程语言中,每个表达式都有一个类型。但是,此定义不适用于动态类型的编程语言。

我个人将表达式定义为一种语法,可以与运算符或函数调用组合以产生更大的表达式。这实际上类似于维基百科对表达的解释:

它是一个或多个常量,变量,函数和运算符的组合,编程语言在有状态环境中解释(根据其特定的优先级和关联规则)并计算以产生(“返回”) )另一个值

但是,问题出在C编程语言中,给定一个函数executeSomething:

void executeSomething(void){
    return;
}

executeSomething()是表达式还是语句?根据我的定义,这是一条语句,因为如Microsoft的C参考语法

中所定义

您不能以任何方式使用类型为void的表达式的(不存在)值,也不能将void表达式(通过隐式或显式转换)转换为除void以外的任何类型

但是同一页上清楚地表明这种语法是一种表达式。

答案 18 :(得分:0)

事实上,这些概念是:

表达式:一种语法类别,可以将其实例评估为一个值。

声明:一种句法类别,其实例可能涉及表达式的求值,并且不能保证求值的结果值(如果有)。

除了在几十年前的FORTRAN最初的上下文之外,公认的答案中的表达式和语句定义显然都是错误的:

  • 表达式可以是未赋值的操作数。永远不会从中产生价值。
    • 可以严格地对非严格评估中的子表达式进行评估。
      • 大多数类似C的语言都有所谓的short-circuit evaluation规则,可以有条件地跳过某些子表达式求值,尽管有副作用也不会改变最终结果。
    • C和某些类似C的语言具有未评估的操作数的概念,甚至可以在语言规范中对其进行规范地定义。此类构造用于绝对避免评估,因此可以静态区分其余上下文信息(例如类型或对齐要求),而无需在程序转换后更改行为。
      • 例如,从不评估用作sizeof运算符的操作数的表达式。
  • 语句与行构造无关。根据语言规范,他们可以做的不仅仅是表达。
    • 现代Fortran作为旧版FORTRAN的直接后代,具有可执行语句不可执行语句 s的概念。
    • 类似地,C ++将声明定义为翻译单元的顶级子类别。 C ++中的声明是声明。 (在C中是不正确的。)还有表达声明,例如Fortran的可执行语句。
    • 为了便于与表达式进行比较,只有“可执行”语句才重要。但是您不能忽略这样一个事实,即语句已经被普遍化为构成这种命令性语言的翻译单元的结构。因此,如您所见,类别的定义相差很大。在这些语言中,(可能)唯一保留的共同属性是语句应按词法顺序进行解释(对于大多数用户,从左到右和从上到下)。 / li>

(顺便说一句,我想在涉及C的材料的答案中添加[需要引用],因为我不记得DMR是否有这样的意见。似乎没有,否则应该没有理由保留该功能C的设计重复:值得注意的是,逗号运算符与语句。)

(以下理由并非对原始问题的直接回答,但我认为有必要澄清此处已回答的问题。)

尽管如此,我们是否需要通用编程语言中特定类别的“声明”呢?

  • 在通常的设计中,不保证语句具有比表达式更多的语义功能。
    • 许多语言已经成功地放弃了语句的概念,以实现简洁,整洁和一致的总体设计。
      • 在这样的语言中,表达式可以执行所有旧式语句可以做的一切:仅在对表达式求值时删除未使用的结果,方法是将结果显式未指定(例如,在R n RS方案中) ),或具有无法通过正则表达式评估得出的特殊值(作为单位类型的值)。
      • 可以用显式序列控制运算符(例如Scheme中的begin或单子结构的语法糖)来代替表达式的词汇顺序规则。
      • 可以将其他类型“语句”的词汇顺序规则作为语法扩展(例如,使用卫生宏)导出,以获得相似的语法功能。 (它实际上可以do more。)
    • 相反,陈述不能具有这样的常规规则,因为它们不构成评估:只有这样的“子陈述评估”概念不常见。 (即使有的话,我仍然怀疑除了复制和粘贴现有的表达式求值规则之外,还有更多其他东西)。
      • 通常,保留语言的语句也将具有用于表达计算的表达式,并且该语句的顶层子类保留用于该子类的表达式求值。例如,C ++将所谓的 expression-statement 作为子类别,并使用 discarded-value expression 评估规则来指定在其中进行全表达式评估的一般情况这样的背景。诸如C#之类的某些语言选择优化上下文以简化用例,但这样会使规范更加specification肿。
  • 对于编程语言的用户来说,语句的重要性可能会使它们进一步混淆。
    • 将语言中的表达规则和陈述规则分开需要学习一种语言。
    • 天真的词序解释隐藏了更重要的概念:表达评估。 (这可能是最有问题的。)
      • 即使语句中的完整表达式的求值受词法顺序约束,子表达式也不(必要)。除了与语句相关的任何规则外,用户最终还应该学习这一点。 (考虑如何使新手了解++i + ++i在C语言中毫无意义。)
      • 诸如Java和C#之类的某些语言进一步限制了子表达式的求值顺序,以允许忽略求值规则。甚至可能会有更多问题。
        • 对于已经学习表达评估思想的用户来说,这似乎过分具体。它还鼓励用户社区遵循语言设计的模糊思维模型。
        • 它使语言规范更加膨胀。
        • 在引入更复杂的基元之前,缺少确定性在评估中的表现性对优化很有害。
      • 一些语言(例如C ++(特别是C ++ 17))指定了更精细的评估规则上下文,以解决上述问题。
        • 它使语言规范大大膨胀。
        • 这与普通用户的简单操作完全相反...

那为什么要声明?无论如何,历史已经是一团糟。似乎大多数语言设计师都不会对他们的选择进行仔细的选择。

更糟糕的是,它甚至给某些类型系统爱好者(他们对PL历史不太熟悉)带来一些误解,认为类型系统必须与操作语义规则的更基本设计有重要关系。

严重的是,在许多情况下,根据类型进行推理并没有那么糟糕,但在这种特殊情况下尤其没有建设性。甚至专家也可以搞砸。

例如,有人强调打字的性质as the central argument against the traditional treatment of undelimited continuations。尽管结论在某种程度上是合理的,并且关于组合函数的见解还可以(but still far too naive to the essense),但该论点并不合理,因为它在实践中完全忽略了_Noreturn any_of_returnable_types(在C11中)编码Falsum严格来说,状态无法预测的抽象机与“崩溃的计算机”并不相同。

答案 19 :(得分:0)

以下是我找到的最简单答案之一的总结。

最初由Anders Kaseorg回答

语句是执行某些操作的完整代码行,而表达式是代码中计算值的任何部分。

表达式可以使用运算符“水平”组合成更大的表达式,而语句只能通过逐个写入或使用块结构“垂直”组合。

每个表达式都可以用作语句(其效果是评估表达式并忽略结果值),但大多数语句不能用作表达式。

http://www.quora.com/Python-programming-language-1/Whats-the-difference-between-a-statement-and-an-expression-in-Python

答案 20 :(得分:-4)

为了改进并验证我之前的答案,编程语言术语的定义应在适用时从计算机科学类型理论中解释。

表达式的类型不是Bottom类型,即它有一个值。语句具有Unit或Bottom类型。

由此可见,语句在创建副作用时只能对程序产生任何影响,因为它既不能返回值,也不会返回不可分配的单元类型的值(在可以存储某些语言,例如C void)或(例如Scala中的语言),以便对语句进行延迟评估。

显然,@pragma/*comment*/没有类型,因此与语句不同。因此,没有副作用的唯一类型的陈述将是非操作。非操作仅用作未来副作用的占位符。由于声明而产生的任何其他行动都会产生副作用。再一次编译器提示,例如@pragma,不是声明,因为它没有类型。

答案 21 :(得分:-9)

最准确地说,声明必须有“副作用”(即be imperative)且表达式必须 类型(即不是底部类型)。

type of a statement是单位类型,但由于Halting定理单位是虚构的,所以我要说bottom type


Void不是底部类型(它不是所有可能类型的子类型)。它以don't have a completely sound type system的语言存在。这可能听起来像一个势利的声明,但完整性such as variance annotations对于编写可扩展的软件至关重要。

让我们看看维基百科在这件事上要说些什么。

https://en.wikipedia.org/wiki/Statement_(computer_science)

  

在计算机编程中,语句是命令式编程语言中最小的独立元素,表示要执行的某些操作

     

许多语言(例如C)区分语句和定义,语句只包含可执行代码和声明标识符的定义,而表达式仅计算值。