ChatGPT解决这个技术问题 Extra ChatGPT

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

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

我发现您选择的答案模棱两可。表达式也有一些作用——它计算出一个值。我提供了一个明确的答案。
@ShelbyMooreIII - 明确且错误。接受的答案以一种非正式的方式措辞,但这种措辞很容易理解——最重要的是,它所传达的意思是准确的。
@JustinMorgan可悲的是,对于大多数当代语言包括类似C的语言,已接受答案中的定义也显然是错误的(“评估为一个值”/“一行代码”):表达式可以在未评估的上下文中使用,而语句什么也没有与线条有关。即使有一些解释,简短的回答也是令人困惑和误导的。
补充一下 - 这个问题实际上是一个错误的二分法。表达式具有可评估的属性,语句也具有但不被期望提供给任何东西,并且 c# 支持 ExpressionStatement 的概念,例如 a=b 而不是 c=(a=b),这是一个表达式不返回值。是否将 ExpressionStatement 称为语句或表达式取决于您。
还值得注意的是,表达式有副作用。如果您正在评估二进制表达式,例如 (a+b) 并且 a 是设置 b 值的属性 getter,那么您需要知道二进制表达式是从左到右进行评估的。

s
scohe001

表达式:求值的东西。示例:1+2/x 语句:执行某事的一行代码。示例:转到 100

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

1 + 2 / X

在 FORTRAN 中是一个错误,因为它什么都不做。你必须用这个表达式做点什么:

X = 1 + 2 / X

FORTRAN 没有我们今天所知道的语法——这个想法是与巴库斯-瑙尔形式 (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 都是没有句法语句的语言——它们只有表达式。甚至类结构化循环和条件形式也被认为是表达式,它们有值——但不是很有趣的值。


如果我在这里没有误解您,您似乎声称“(setf(第三个foo)'goose)”是一个表达式,而不是一个语句,因为它是Lisp,它“没有语句”,并且因为 Lisp 比 C 早了十多年,C 是“最早模糊 [表达式和语句之间] 界限的流行语言”。可以给我详细解释一下吗?
@Curt Sampson,您是否将其作为一个单独的问题提出?
如果我没记错的话,callfunc(x = 2);x 传递给 callfunc,而不是 2。如果 x 是浮点数,将调用 callfunc(float),而不是 callfunc(int)。在 C++ 中,如果您将 x=y 传递给 func,并且 func 获取一个引用并更改它,它会更改 x,而不是 y
在上面的答案中,写道“Haskell,......都是没有句法语句的语言 - 它们只有表达式”。我很好奇为什么 haskell 中的 where 子句被认为是表达式而不是语句。 learnyouahaskell.com/syntax-in-functions#where
@skgbanga 我相信 where 实际上是函数声明的一部分,而不是表达式或语句。
M
Mark Harrison

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


关于什么是陈述的逻辑不正确。声明式程序也可以执行,但声明式程序没有语句。一个语句是并且确实是"side-effects",即是命令式的。参看。 my answer
P
Patrick

表达式是返回值的东西,而语句则不。

举些例子:

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

两者之间的大交易是您可以将表达式链接在一起,而不能将语句链接在一起。


当然语句可以链接。 {stmt1; stmt2; stmt3;} 是一个链,它本身也是一个(复合)语句。
foo.voidFunc(1); 是具有 void 值的表达式。 whileif 是语句。
我很好奇声明的非链接。会不会像“如果(x > 1)返回;”被认为将两个语句链接在一起?
@SimonTewsi 我相信 return 被认为是一个子语句。
@SimonTewsi 这里的 return 语句隐含在 if 语句的块内,因此它是 if 语句的一部分,而不是与其链接。编译器允许我们在这里省略大括号,因为它是一个单行块。
M
Mike Stone

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

因此,表达式可以在语句中使用,但反之则不行。

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


M
Matthew Schinckel

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

命令式语言(Fortran、C、Java 等)强调构建程序的语句,并将表达式作为一种事后的想法。函数式语言强调表达式。纯函数式语言具有如此强大的表达式,以至于可以完全消除语句。


M
Mark Cidade

可以对表达式求值以获取值,而语句不返回值(它们是 void 类型)。

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

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

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

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

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


语句的类型是底部类型。 Void 不是底部类型。请参阅my answer
null 类型 不是底部类型(null 的单个值)吗? void 会不会更像 unit 类型(但它的单个值不可访问)?
如果 void 是从不返回的函数的返回类型(例如,throw 是错误的函数),则它是 bottom type。否则 voidunit type。您是正确的,不能发散的语句具有单位类型。但是可以发散的陈述是底部类型。由于停止定理,我们通常无法证明函数不发散,所以我认为单位是虚构的。底部类型不能有值,因此它不能有单个值 null
关于我三年前所说的,我不知道我是否仍然认为语句具有 void 类型或任何类型。在我熟悉的基于语句的语言中,只有 values 以及存储或返回值的任何内容(例如,表达式、变量、成员和函数)可以具有类型。我通常认为底部类型是空集(没有值),因此本体上不存在的任何东西都会有这种类型。 null 值实际上是一个伪值,表示引用引用了不存在的东西。
马克我很欣赏你的回应的合理性。你基本上是从我嘴里说出来的话我希望很清楚我向你承认你提高单位点是正确的。我想我们同意。我不想费心提这个,但似乎这里有些人认为我是消极的。我只是想成为事实。
M
Matthew Schinckel

简单地说:表达式计算为一个值,而语句则没有。


那么声明有什么作用呢?没有什么?
它可以做某事,但它不会评估任何东西。即,您不能将其结果分配给变量,而可以使用表达式。
因此,一个声明必须有副作用,正如我被严重否决的答案所述。语句可能还有什么其他效用?即使一个 NO-OP 被认为是一个语句(它只是语法中的“语句”,而不是语义层,因为它在解析后被删除,而语义是我们在这里讨论的内容),它也无法解释什么是哎呀,声明的一般用途是。
@ShelbyMooreIII 语句不需要做任何事情或有副作用。例如,{} 是一个语句。把这个词放在吓人的引号中并不会改变这一点。语句是具有语义的句法结构。没有“语义层”之类的东西——您似乎指的是执行。你说你试图准确,但你没有做到这一点。您对“选民的无知”的抱怨纯属自欺欺人;您没有关于投票者的心理状态的信息。
是的,除了智力上的不诚实之外,每个人都是错的。 {} 被定义为 C# 语言规范中的语句。
佚名

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

最重要:一切都返回一个值

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

在基于表达式的语言中,一切都返回一个值。起初这可能有点奇怪—— (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 == "你好,世界!" 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
)

E
Ely

我对这里的任何答案都不满意。我查看了 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 是一个标记语句

选择语句是 if/else, case

迭代语句是 while、do...while、for (...)

跳转语句是 break、continue、return(可以返回表达式)、goto

声明语句是声明的集合

try-block 是表示 try/catch 块的语句

可能还有更多的语法

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

expression:
        assignment-expression
        expression "," assignment-expression
assignment-expression:
        conditional-expression
        logical-or-expression assignment-operator initializer-clause
        throw-expression

表达式通常是或包含赋值

条件表达式(听起来有误导性)指的是运算符(+、-、*、/、&、|、&&、||、...)的使用

扔表情 - 嗯? throw 子句也是一个表达式


我最近开始对这个话题感兴趣,不幸的是,stackoverflow 上经常出现这种情况,这个低投票的答案可能是列表中唯一“正确”的答案。简单地说,'a=b' 可以是表达式或语句,具体取决于上下文。换句话说,决定表达式与语句的不是语法元素的某些内在属性,而是由编译器使用的隐含或显式语法定义的上下文。
D
Daniel Earwicker

语句是表达式的一种特殊情况,具有 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);
}

编译器能否处理 TResultvoid 的可能性?是的。它所要做的就是要求 return 后跟一个 void 类型的表达式。 default(void) 的结果将是 void 类型,传入的 func 需要采用 Func<TValue, void> 形式(相当于 Action<TValue>)。

许多其他答案暗示您不能像使用表达式那样将语句链接起来,但我不确定这个想法来自哪里。我们可以将出现在语句之后的 ; 视为二元中缀运算符,它采用两个 void 类型的表达式并将它们组合成一个 void 类型的表达式。


语句不是表达式的特殊情况。在某些语言(即大多数 C 继承者)中,实际上是相反的。
A
Andaquin

语句 -> 顺序遵循的说明 表达式 -> 返回值的求值

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

表达式乍一看并不暗示和执行顺序,它们的目的是评估和返回一个值。在命令式编程语言中,表达式的求值是有顺序的,但这只是因为命令式模型,而不是它们的本质。

语句示例:

for
goto
return
if

(所有这些都意味着将执行行(语句)推进到另一行)

表达式示例:

2+2

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


@AustinHenley 对此没有要求。事实上,一个表达式肯定会产生副作用。
F
FrankHB

这些概念的事实基础是:

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

语句:一个语法类别,其实例可能涉及表达式的评估,并且评估的结果值(如果有)不保证可用。

除了早期几十年 FORTRAN 的最初背景之外,公认答案中的表达式和语句的定义显然都是错误的:

表达式可以是未赋值的操作数。价值永远不会从它们中产生。非严格求值中的子表达式绝对可以不求值。大多数类 C 语言都有所谓的短路评估规则,可以有条件地跳过一些子表达式评估,尽管有副作用,但不会改变最终结果。 C 和一些类 C 语言具有未计算操作数的概念,甚至可以在语言规范中进行规范定义。这样的构造用于明确地避免评估,因此可以在不改变程序翻译后的行为的情况下静态区分剩余的上下文信息(例如类型或对齐要求)。例如,用作 sizeof 运算符的操作数的表达式永远不会被计算。

非严格求值中的子表达式绝对可以不求值。大多数类 C 语言都有所谓的短路评估规则,可以有条件地跳过一些子表达式评估,尽管有副作用,但不会改变最终结果。

大多数类 C 语言都有所谓的短路评估规则,可以有条件地跳过一些子表达式评估,尽管有副作用,但不会改变最终结果。

C 和一些类 C 语言具有未计算操作数的概念,甚至可以在语言规范中进行规范定义。这样的构造用于明确地避免评估,因此可以在不改变程序翻译后的行为的情况下静态区分剩余的上下文信息(例如类型或对齐要求)。例如,用作 sizeof 运算符的操作数的表达式永远不会被计算。

例如,用作 sizeof 运算符的操作数的表达式永远不会被计算。

语句与行结构无关。根据语言规范,它们可以做的不仅仅是表达式。现代 Fortran 作为旧 FORTRAN 的直系后代,具有可执行语句和不可执行语句的概念。同样,C++ 将声明定义为翻译单元的顶级子类别。 C++ 中的声明是一个语句。 (在 C 中不是这样。)还有表达式语句,如 Fortran 的可执行语句。为了与表达式进行比较,只有“可执行”语句很重要。但是你不能忽略这样一个事实,即语句已经被概括为构成这种命令式语言的翻译单元的构造。因此,如您所见,该类别的定义差异很大。这些语言中(可能)唯一保留的共同属性是语句应按词汇顺序(对于大多数用户,从左到右和从上到下)进行解释。

现代 Fortran 作为旧 FORTRAN 的直系后代,具有可执行语句和不可执行语句的概念。

同样,C++ 将声明定义为翻译单元的顶级子类别。 C++ 中的声明是一个语句。 (在 C 中不是这样。)还有表达式语句,如 Fortran 的可执行语句。

为了与表达式进行比较,只有“可执行”语句很重要。但是你不能忽略这样一个事实,即语句已经被概括为构成这种命令式语言的翻译单元的构造。因此,如您所见,该类别的定义差异很大。这些语言中(可能)唯一保留的共同属性是语句应按词汇顺序(对于大多数用户,从左到右和从上到下)进行解释。

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

(以下理由不是对原始问题的直接回应,但我觉得有必要澄清这里已经回答的问题。)

然而,我们是否需要通用编程语言中的特定类别的“语句”是值得怀疑的:

在通常的设计中,不能保证语句比表达式具有更多的语义能力。许多语言已经成功地放弃了语句的概念,以获得干净、整洁和一致的整体设计。在这样的语言中,表达式可以做旧式语句可以做的所有事情:在计算表达式时删除未使用的结果,要么明确地不指定结果(例如在 RnRS 方案中),要么具有特殊值(作为一个单位类型)不能从正常的表达式评估中产生。表达式求值的词汇顺序规则可以被显式的序列控制操作符(例如,在 Scheme 中的 begin)或单子结构的语法糖代替。其他类型的“语句”的词汇顺序规则可以作为句法扩展(例如使用卫生宏)来获得类似的句法功能。 (而且它实际上可以做得更多。)相反,语句不能有这样的常规规则,因为它们不构成评估:没有“子语句评估”这样的常见概念。 (即使有,我怀疑除了从现有的表达式求值规则中复制和粘贴之外,还有更多的东西。)通常,语言保留语句也会有表达式来表达计算,并且语句有一个顶级子类别保留到该子类别的表达式评估。例如,C++有所谓的表达式语句作为子类别,并使用丢弃值表达式评估规则来指定这种上下文中全表达式评估的一般情况。某些语言(如 C#)选择细化上下文以简化用例,但它使规范更加膨胀。

许多语言已经成功地放弃了语句的概念,以获得干净、整洁和一致的整体设计。在这样的语言中,表达式可以做旧式语句可以做的所有事情:在计算表达式时删除未使用的结果,要么明确地不指定结果(例如在 RnRS 方案中),要么具有特殊值(作为一个单位类型)不能从正常的表达式评估中产生。表达式求值的词汇顺序规则可以被显式的序列控制操作符(例如,在 Scheme 中的 begin)或单子结构的语法糖代替。其他类型的“语句”的词汇顺序规则可以作为句法扩展(例如使用卫生宏)来获得类似的句法功能。 (它实际上可以做得更多。)

在这样的语言中,表达式可以做旧式语句可以做的所有事情:在计算表达式时删除未使用的结果,要么明确地不指定结果(例如在 RnRS 方案中),要么具有特殊值(作为一个单位类型)不能从正常的表达式评估中产生。

表达式求值的词汇顺序规则可以被显式的序列控制操作符(例如,在 Scheme 中的 begin)或单子结构的语法糖代替。

其他类型的“语句”的词汇顺序规则可以作为句法扩展(例如使用卫生宏)来获得类似的句法功能。 (它实际上可以做得更多。)

相反,语句不能有这样的常规规则,因为它们不构成评估:只是没有“子语句评估”这样的常见概念。 (即使有,我怀疑除了从现有的表达式求值规则中复制和粘贴之外,还有更多的东西。)通常,语言保留语句也会有表达式来表达计算,并且语句有一个顶级子类别保留到该子类别的表达式评估。例如,C++有所谓的表达式语句作为子类别,并使用丢弃值表达式评估规则来指定这种上下文中全表达式评估的一般情况。某些语言(如 C#)选择细化上下文以简化用例,但它使规范更加膨胀。

通常,语言保留语句也将具有表达计算的表达式,并且有一个顶级子类别的语句保留用于该子类别的表达式评估。例如,C++有所谓的表达式语句作为子类别,并使用丢弃值表达式评估规则来指定这种上下文中全表达式评估的一般情况。某些语言(如 C#)选择细化上下文以简化用例,但它使规范更加膨胀。

对于编程语言的用户来说,语句的重要性可能会使他们更加困惑。语言中表达式和语句规则的分离需要更多的努力来学习一门语言。朴素的词序解释隐藏了更重要的概念:表达式求值。 (这可能是最有问题的。)即使语句中完整表达式的评估受词法顺序的约束,子表达式也不是(必然)。除了与语句相关的任何规则外,用户最终应该了解这一点。 (考虑如何让新手明白 ++i + ++i 在 C 中没有意义。)Java 和 C# 等一些语言进一步限制子表达式的求值顺序,以允许忽略求值规则。问题可能更大。对于已经了解表达式评估概念的用户来说,这似乎是过度指定的。它还鼓励用户社区遵循语言设计的模糊心理模型。它使语言规范更加膨胀。在引入更复杂的原语之前,由于缺少评估的不确定性的表达性,这对优化是有害的。像 C++(特别是 C++17)这样的一些语言指定了更微妙的评估规则上下文,作为上述问题的折衷方案。它使语言规范膨胀了很多。这完全违背了普通用户的简单性......

语言中表达式和语句规则的分离需要更多的努力来学习一门语言。

朴素的词序解释隐藏了更重要的概念:表达式求值。 (这可能是最有问题的。)即使语句中完整表达式的评估受词法顺序的约束,子表达式也不是(必然)。除了与语句相关的任何规则外,用户最终应该了解这一点。 (考虑如何让新手明白 ++i + ++i 在 C 中没有意义。)Java 和 C# 等一些语言进一步限制子表达式的求值顺序,以允许忽略求值规则。问题可能更大。对于已经了解表达式评估概念的用户来说,这似乎是过度指定的。它还鼓励用户社区遵循语言设计的模糊心理模型。它使语言规范更加膨胀。在引入更复杂的原语之前,由于缺少评估的不确定性的表达性,这对优化是有害的。像 C++(特别是 C++17)这样的一些语言指定了更微妙的评估规则上下文,作为上述问题的折衷方案。它使语言规范膨胀了很多。这完全违背了普通用户的简单性......

即使是语句中完整表达式的求值也受到词法顺序的约束,子表达式则不是(必要的)。除了与语句相关的任何规则外,用户最终应该了解这一点。 (考虑如何让新手明白 ++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 进行编码. 而且严格来说,状态不可预测的抽象机器并不等同于“崩溃的计算机”。


S
Sujit

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 都是标识局部变量的简单名称。当在表达式中使用这些变量时,将检索变量的值并将其用于表达式。


我宁愿写 if(number >= 0) return true; else return false; 甚至更好的 bool? IsPositive(int number) { if(number > 0) return true; else if(number < 0) return false; else return null;} :)
v
vfclists

我更喜欢 statement 在形式逻辑意义上的含义。它可以改变计算中一个或多个变量的状态,从而能够对它们的值做出真或假的陈述。

我想,当引入新的术语或新词、现有词被“重新利用”或用户不了解他们所描述的现有、已建立或“适当”的术语时,计算世界和一般科学总会出现混乱


M
MadNik

这是我找到的最简单答案之一的摘要。

最初由 Anders Kaseorg 回答

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

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

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

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


n
ncmathsadist

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

x = 5

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

(x + 5)/9.0

读作“x 加 5 全部除以 9.0”。这不是一个完整的句子。该声明

while k < 10: 
    print k
    k += 1

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


while 是一些语言的表达式,例如 Scala。您将语法与打字混为一谈。请参阅my answer
这是 scala 中的 while 循环: tutorialspoint.com/scala/scala_while_loop.htm 带有谓词且没有主体的循环不是语法完整的句子。这不是一个完整的表达。您需要主体来完成它作为一个表达式。
带有主体的 while 在 Scala 中仍然是一个表达式。如果它产生副作用,它也可能是一个声明,这是我严重否决的答案所允许的(表达式也可以是一个声明)。我的答案是唯一正确的。不能理解的读者请见谅。
你说的语法完整是什么意思?在 C 语言中,(x + 5)/9.0 绝对可以作为一个语句独立存在。此外,如果通过语法完整,您的意思是一个有效的程序,C 不允许语句作为单个程序独立存在。
语法完整:构成一个完整的句子。
X
Xwtek

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

维基百科类似地定义了语句这个词

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

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

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

编程语言中的表达式是可以被评估以确定其值的句法实体

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

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

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

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

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

void executeSomething(void){
    return;
}

executeSomething() 是表达式还是语句?根据我的定义,它是一个语句,因为在微软的 C 引用语法中定义,

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

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


G
Gk Mohammad Emon

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

if(a>=0)
printf("Hello Humen,I'm a statement");

另一方面,表达式返回或计算一个新值。例如 -

 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. 

V
Vlad Bezden

Expression

一段可以被评估为某个值的语法。换句话说,表达式是表达式元素的集合,如文字、名称、属性访问、运算符或函数调用,它们都返回一个值。与许多其他语言相比,并非所有语言结构都是表达式。还有一些语句不能用作表达式,例如while。赋值也是语句,而不是表达式。

Statement

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


B
Brad Larson

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

表达式具有不同于底部类型的类型,即它具有值。语句具有 Unit 或 Bottom 类型。

由此可见,一个语句只有在产生副作用时才能在程序中产生任何影响,因为它要么不能返回值,要么只返回不可赋值的 Unit 类型的值(在某些语言中,例如C 的 void) 或(例如在 Scala 中)可以存储以用于语句的延迟评估。

显然 @pragma/*comment*/ 没有类型,因此与语句不同。因此,唯一没有副作用的语句类型是非操作。非操作仅可用作未来副作用的占位符。由于声明而导致的任何其他操作都将是副作用。同样,编译器提示(例如 @pragma)不是语句,因为它没有类型。


区别与表达式的类型无关。在许多语言中,语法定义的语句根本没有类型。尽管我在理论上不反对根据这些术语分配类型,但对 @pragma/*comment*/ 的不同处理在逻辑上是不一致的。
C
Community

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

type of a statement 是单位类型,但由于停止定理,单位是虚构的,所以可以说 bottom type

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

让我们看看维基百科对这个问题有什么看法。

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

在计算机编程中,语句是命令式编程语言中最小的独立元素,它表示要执行的某些操作。许多语言(例如C)对语句和定义进行了区分,语句仅包含可执行代码,定义声明标识符,而表达式仅计算值。


声明不必有副作用。例如,在 python 中 pass 是一个语句。这是一个空操作,它不会评估任何东西。
-1 这是错误的。声明没有必须有副作用。请参阅 C# Language Specification 的第 1.5 节。它不仅没有指定语句必须有副作用,而且还列出了几个可能有没有副作用的语句。
@NullUserException 我读了那部分。声明、表达式、选择、迭代和跳转语句都可能产生副作用。但是如果有一个 RT 表达式,那么它就不是一个语句。我意识到规范是将表达式等同于语句,但是这个问题询问了它们之间的区别。所以要么这个问题没有答案,要么你太从字面上理解了 C# 规范。投票最多的答案是试图说出我做了什么。但是“做某事”是没有意义的。 “副作用”是说“做某事”的有意义的方式。我们必须思考,而不仅仅是反刍。
@ShelbyMooreIII 你是对的。官方文档是错误的。 Marc Gravell 和 Jon Skeet,他们可能是 Eric Lippert 之外最受尊敬的 C# 海报,他们错了。我和所有其他对你投反对票并留下评论解释我们立场的人都是错误的。你说的对。你显然是唯一知道他们在说什么的人,因为你比 SO 的所有其他人都聪明得多。
了解语句、表达式、强制转换、转换等概念的精确定义对 99.999% 的日常编程任务的影响为零。想想那个。另外:most people don't care about Haskell and Scala.