一个表达式和一个语句有什么区别?

9
我知道这可能是重复的问题,但我没有找到足够的答案来回答我的例子。总体而言,语句和表达式之间有什么区别?这是我还没有完全区分的区别。我知道通常表达式被认为是任何返回值的东西,比如文字或函数。语句通常被说成是解释器的命令,比如“打印这个”或“做……直到”。然而,我不明白。
难道说打印(print)不是一个表达式吗,因为它是一个将输入(input)打印输出(output)的函数吗?同时,人们通常说x = 1是一个语句,但这不可以被视为一种表达式,其中赋值运算符是作用于两个输入并且输出是x指向1的函数吗?最后,if……else这样的流程控制结构是否可以被看作是一个三参数函数,在第一个输入的真值基础上返回其他参数之一,使其成为一个表达式?
我可能会感到困惑,因为我有LISP的背景,那里的一切都是表达式。似乎我无法摆脱这样的想法,即大多数编程结构在本质上都是表达式。因此,有人能解释一下所谓的语句和表达式之间的真正区别吗?

答案比实际更具哲学意义。语句通常具有副作用,而表达式通常更具代数性质。在纯函数式编程语言中,没有语句。也就是说,即使执行副作用(例如写入文件)的那些操作也是用表示所有变异(包括失败)的值来表示的。命令式语言利用语句执行任意代码,例如将值分配给变量或将数据打印到控制台,并可能通过异常传播来传达失败信息。 - cwharris
区别取决于你所讨论的编程语言。在C语言中,x = 1是一个表达式,而x = 1;则是一个语句。在Ada语言中,x := 1不是一个表达式,但x := 1;是一个语句。 - Keith Thompson
3个回答

10
“表达式”和“语句”的定义,甚至这两个概念是否存在,都是特定语言及其语法所特有的。一个“语句”是一些可执行的代码1,它不出现在表达式环境中;而“表达式”是指出现在可以通过表达式替换来消耗结果值的上下文中的代码。{这是一个非常宽泛的“定义”,但没有一种语言是万能的。虽然有些语言对副作用何时发生进行了严格的限制,而没有结果或副作用的代码是无用的,但我认为讨论这些并不是区别的基础。}例如,在C语言中,我们看看printf函数。这是一个具有副作用并返回一个值的函数;通常返回值会被忽略。因此,printf既可以出现为语句,也可以出现为表达式。
printf("Hello world!");

和一个表达式

if (8 == printf("Hello %s!", name)) { // ..

在 C 语言中,带有返回类型 void 的函数调用只能出现在语句上下文中,但这是由类型系统而非解析器强制执行的。

同样,在 JavaScript 中,x = 1;x = (y = 2); 这两行代码中,x = .. 是一个语句,而 y = 2 则是一个返回了值的表达式。

在这两个示例中,我们可以看到是语法产生式确定了它被视为语句还是表达式。

相比之下,Ruby 可以将“顶层”赋值视为表达式:

[1].map {|x| x = 2}

现在让我们来看看Python(2.x)。在这种情况下,print是一个语句,这就是为什么这些代码分别起作用和不起作用的原因:
print "Look ma, no parenthesis!"
x = lambda y: print "Whoops!"       # invalid, print not an expression

if结构中,它们是语句还是表达式?同样地,这取决于具体的编程语言。 在C和Java中,它们明显是语句:不能将其用作值的替代品。
另一方面,Scala(和Ruby)允许使用这样的流程控制结构作为表达式,尽管它们也可以出现为语句:
var emotionalResponse = if (color == "green") {
                          log.cheer()
                          new Cheering()
                        } else {
                          new Tears()
                        }

哇,这是很多内容 - 而且还远远不够全面。但是,回到“定义”,考虑到上面的各种例子,可以重新表述如下:

如果所讨论的结构可以出现在需要值的位置(例如,在赋值的右侧,作为函数参数,作为另一个表达式的输入),那么它可以被视为表达式;当处于这样的上下文中时,它绝对是一个表达式。如果该结构出现在无法通过替换访问该值的位置,则它是(或者更确切地说,可以充当)语句。


1 另一类需要进行条件处理的产物是声明,例如C语言中的函数声明或Java中的类定义,可以说它们并不是语句;由于下面的内容已经很分散了,这就是本注释的全部。


printf() 不是一个语句。函数调用 才是语句:printf() 只是被调用的函数。 - user207421
@EJP“是一个函数..可以出现为”-虽然我对更清晰的建议持开放态度。 - user2864740
1
在C语言中,printf("Hello")是一种表达式而不是语句。而printf("Hello");则是一种语句。表达式语句是一种特定的语句类型,由一个(可选)表达式后跟分号组成。(出于某种原因,空语句也被视为一种表达式语句。) - Keith Thompson

4
一个表达式产生一个值,而一个语句则执行某些操作。
例如,在C语言中,return a+5;是一个语句,它从表达式a+5中得到一个值,并将该值返回给调用者,从而退出函数。
打印(print)是表达式还是语句取决于编程语言。
BASIC有print,它不像函数那样被处理,而是由编译器作为特殊情况处理。同样,Pascal有writewriteln,它们也是特殊情况,不是函数调用。另一方面,C有puts()和printf(),它们都是函数,并允许程序员编写类似的函数以相同的方式工作。

一个表达式可以“做某事”。 - user2864740
@user2864740 或许有必要区分具有副作用和没有副作用的表达式。 - JPC
1
@JPC 我认为回答这个问题并不需要讨论表达式的副作用(尽管它很适合),语句和表达式之间的区别是一种语法产生规则 - user2864740

2
答案更多的是哲学性的而不是实用性的。语句通常具有副作用,而表达式通常更具代数性质。
在纯函数式语言中,没有语句。也就是说,即使执行副作用(例如写入文件),也是以表示所有变化(包括失败)的值的形式表达的(理想情况下)。
命令式语言利用语句执行任意代码,例如将值分配给变量或打印到控制台,并可能通过异常传播来通信失败。
纯函数表达式仅通过将这些变化公开为值本身来减少处理变化的认知负担。将其与模式匹配混合使用,您被迫明确处理所有成功和失败的情况,并因此将它们转化为值,明确映射或过滤任何失败。
命令式语句不需要将失败公开为值,因此始终存在着任意代码以容易被忽视的方式存在于不知情的方法中的可能性。语句,例如方法调用或变量赋值(x = y = z()),通常可以符合表达式的模式,同时执行任意代码并未能将失败作为值进行通信。
最终,你无法避免突变。但是,你可以使其更加明确。这意味着语句和表达式在哲学上存在差异,但两者都旨在回答同一个问题——在代码执行的上下文中,我们应该如何暴露突变?

这应该是被接受的答案。谢谢你。干得好! - Timur Fayzrakhmanov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接