Joe*_*sky 515
表达式:评估值的东西.示例:1 + 2/x
语句:执行某些操作的代码行.示例:GOTO 100
在最早的通用编程语言中,如FORTRAN,区别非常清晰.在FORTRAN中,声明是一个执行单元,你做了一件事.它不被称为"线"的唯一原因是因为有时它跨越多条线.表达式本身无法做任何事情......你必须将它分配给变量.
1 + 2 / X
Run Code Online (Sandbox Code Playgroud)
是FORTRAN中的错误,因为它不执行任何操作.你必须对那个表达做一些事情:
X = 1 + 2 / X
Run Code Online (Sandbox Code Playgroud)
FORTRAN没有我们今天所知的语法 - 这个想法与Backus-Naur Form(BNF)一起被发明,作为Algol-60定义的一部分.在那一点上,语义上的区别("有一个值"与"做某事")在语法中得到了体现:一种短语是表达式,另一种是语句,解析器可以区分它们.
后来语言的设计者模糊了这种区别:他们允许语法表达式做事,并且他们允许具有值的句法语句.仍然存在的最早的流行语言示例是C.C的设计者意识到,如果允许您评估表达式并丢弃结果,则不会造成任何伤害.在C语言中,每个语法表达式都可以通过在末尾添加分号来形成语句:
1 + 2 / x;
Run Code Online (Sandbox Code Playgroud)
虽然绝对不会发生任何事情,但这是完全合法的陈述.类似地,在C中,表达式可能有副作用 - 它可以改变某些东西.
1 + 2 / callfunc(12);
Run Code Online (Sandbox Code Playgroud)
因为callfunc
可能只是做一些有用的事情
一旦允许任何表达式成为语句,您也可以在表达式中允许赋值运算符(=).这就是为什么C让你做的事情
callfunc(x = 2);
Run Code Online (Sandbox Code Playgroud)
这将计算表达式x = 2(将值2赋值给x),然后将该值(2)传递给函数callfunc
.
表达式和语句的这种模糊发生在所有C衍生物(C,C++,C#和Java)中,它们仍然有一些语句(如while
),但几乎允许任何表达式用作语句(仅在C#中赋值,调用,递增和递减表达式可用作语句;请参阅Scott Wisniewski的答案).
有两个"语法类别"(这是事物语句和表达式的技术名称)可能导致重复工作.例如,C有两种形式的条件,即语句形式
if (E) S1; else S2;
Run Code Online (Sandbox Code Playgroud)
和表达形式
E ? E1 : E2
Run Code Online (Sandbox Code Playgroud)
有时人们想要不存在的重复:例如,在标准C中,只有一个语句可以声明一个新的局部变量 - 但这种能力足够有用,GNU C编译器提供了一个GNU扩展,使表达式能够声明一个局部变量也是如此.
其他语言的设计者不喜欢这种重复,他们很早就看到如果表达式既有副作用也有值,那么语句和表达式之间的句法区别并不是那么有用 - 所以他们摆脱了它.Haskell,Icon,Lisp和ML都是没有语法语句的语言 - 它们只有表达式.即使是类结构化循环和条件形式也被认为是表达式,它们具有值 - 但不是非常有趣的.
Mar*_*son 21
请注意,在C中,"="实际上是一个运算符,它执行两项操作:
这是ANSI C语法的摘录.您可以看到C没有很多不同类型的语句......程序中的大多数语句都是表达式语句,即最后带分号的表达式.
statement
: labeled_statement
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
;
expression_statement
: ';'
| expression ';'
;
Run Code Online (Sandbox Code Playgroud)
http://www.lysator.liu.se/c/ANSI-C-grammar-y.html
Pat*_*ick 14
表达式是返回值的东西,而语句则不是.
举些例子:
1 + 2 * 4 * foo.bar() //Expression
foo.voidFunc(1); //Statement
Run Code Online (Sandbox Code Playgroud)
两者之间的大问题是你可以将表达式链接在一起,而语句不能被链接.
为了解释表达式与语句的可组合性(可链接性)的重要差异,我最喜欢的参考是John Backus的图灵奖论文,可以从冯·诺依曼风格中解放出编程吗?.
命令式语言(Fortran,C,Java,...)强调结构化程序的语句,并将表达式作为一种后思.功能语言强调表达. 纯函数式语言具有如此强大的表达式,而语句可以完全消除.
我对这里的任何答案都不满意。我查看了 C++ 的语法(ISO 2008)。然而,也许出于教学和编程的目的,答案可能足以区分这两个元素(尽管现实看起来更复杂)。
语句由零个或多个表达式组成,但也可以是其他语言概念。这是语法的扩展巴科斯诺尔形式(语句摘录):
statement:
labeled-statement
expression-statement <-- can be zero or more expressions
compound-statement
selection-statement
iteration-statement
jump-statement
declaration-statement
try-block
Run Code Online (Sandbox Code Playgroud)
我们可以看到 C++ 中被视为语句的其他概念。
case
例如是一个标记语句if
if/else
,case
while
, do...while
,for (...)
break
, continue
, return
(可以返回表达式),goto
try/catch
块的语句这是显示表达式部分的摘录:
expression:
assignment-expression
expression "," assignment-expression
assignment-expression:
conditional-expression
logical-or-expression assignment-operator initializer-clause
throw-expression
Run Code Online (Sandbox Code Playgroud)
+
, -
, *
, /
, &
, |
, &&
, ||
, ...)throw
子句也是一个表达式可以计算表达式以获取值,而语句不返回值(它们的类型为void).
函数调用表达式当然也可以被认为是语句,但除非执行环境有一个特殊的内置变量来保存返回的值,否则无法检索它.
面向语句的语言要求所有过程都是语句列表.面向表达式的语言,可能是所有函数式语言,是表达式列表,或者在LISP的情况下,是表示表达式列表的一个长S表达式.
尽管两种类型都可以组合,但只要类型匹配,大多数表达式都可以任意组合.每种类型的语句都有自己的组合其他语句的方式,如果他们可以做到这一切.Foreach和if语句要求单个语句或所有从属语句一个接一个地进入语句块,除非子语句允许其自己的子语句.
语句还可以包含表达式,其中表达式实际上不包含任何语句.但是,一个例外是lambda表达式,它表示一个函数,因此可以包含函数可以包含的任何内容,除非该语言仅允许有限的lambdas,如Python的单表达式lambda.
在基于表达式的语言中,您只需要一个函数的单个表达式,因为所有控制结构都返回一个值(其中很多都返回NIL).由于函数中最后一次计算的表达式是返回值,因此不需要return语句.
小智 5
关于基于表达式的语言的一些事情:
最重要的是:一切都有回报
用于分隔代码块和表达式的大括号和大括号没有区别,因为一切都是表达式。但这并不妨碍词法作用域:例如,可以为包含其定义的表达式以及其中包含的所有语句定义局部变量。
在基于表达式的语言中,一切都会返回一个值。一开始这可能有点奇怪——(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()
orTwiddleYourThumbs()
将返回某种普通值,例如 OK、Done 或 Success。(FOR i = 1 TO 10 DO (print i))
,for 循环的值为“10”,它会导致表达式(print i)
被计算 10 次,每次都将 i 作为字符串返回。最后一次返回10
,我们最终的答案通常需要稍微改变思维方式才能充分利用基于表达式的语言,因为一切都是表达式这一事实使得“内联”很多东西成为可能
举个简单的例子:
Run Code Online (Sandbox Code Playgroud)FOR i = 1 to (IF MyString == "Hello, World!" THEN 10 ELSE 5) DO ( LotsOfCode )
是非基于表达式的完全有效的替代
Run Code Online (Sandbox Code Playgroud)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
)
Run Code Online (Sandbox Code Playgroud)
这些概念的事实上的基础是:
表达式:一个语法类别,其实例可以计算为一个值。
语句:一个语法类别,其实例可能涉及表达式的求值,并且求值的结果值(如果有)不保证可用。
除了几十年前 FORTRAN 的最初背景之外,公认答案中表达式和语句的定义显然都是错误的:
sizeof
永远不会被求值。(顺便说一句,我想在有关 C 的材料的答案中添加[需要引用],因为我不记得 DMR 是否有这样的意见。似乎没有,否则应该没有理由在 C 的设计中保留功能重复: 值得注意的是,逗号运算符与语句。)
(以下理由不是对原始问题的直接回答,但我觉得有必要澄清一些已经回答过的问题。)
然而,我们是否需要通用编程语言中的特定类别的“语句”是值得怀疑的:
begin
在Scheme 中)或一元结构的语法糖来代替。++i + ++i
(考虑一下如何让一个新手明白C语言中无意义的点。)那么为什么要声明呢?无论如何,历史已经是一团糟了。似乎大多数语言设计者并没有仔细考虑他们的选择。
更糟糕的是,它甚至给一些类型系统爱好者(对 PL 历史不太熟悉)带来一些误解,认为类型系统必须与操作语义上更重要的规则设计有关。
说真的,在许多情况下,根据类型进行推理并没有那么糟糕,但在这种特殊情况下尤其没有建设性。即使是专家也可能把事情搞砸。
例如,有人强调良好类型的本质是反对传统处理无限延续的核心论点。虽然结论有些合理,关于组合函数的见解也不错(但对于本质来说仍然太天真),但这个论点并不合理,因为它完全忽略了实践中的“侧通道”方法,例如_Noreturn any_of_returnable_types
(在 C11 中)进行编码Falsum
。 严格来说,状态不可预测的抽象机器并不等同于“崩溃的计算机”。