表达与陈述
我问的是关于c#的问题,但我认为其他大多数语言也是如此。
有人对表达式和语句有一个很好的定义,区别是什么?
解决方案
回答
可以对表达式求值以获得值,而语句不返回值(它们的类型为void)。
当然,函数调用表达式也可以视为语句,但是除非执行环境具有特殊的内置变量来保存返回的值,否则无法检索它。
面向语句的语言要求所有过程都是语句列表。面向表达式的语言(可能是所有功能语言)是表达式列表,或者在LISP的情况下,是一种长S表达式,表示表达式列表。
尽管两种类型都可以组成,但是只要类型匹配,大多数表达式都可以任意组成。每种类型的语句都有自己的方式来组合其他语句,如果它们能做到的话。 Foreach和if语句只需要一个语句,或者所有从属语句都一个接一个地放在语句块中,除非这些子语句允许使用自己的子语句。
语句也可以包含表达式,其中表达式实际上并不包含任何语句。不过,有一个例外是表示函数的lambda表达式,因此可以包含函数可以包含的任何内容,除非该语言仅允许有限的lambda,例如Python的单表达式lambda。
在基于表达式的语言中,我们所需要的只是一个函数的单个表达式,因为所有控制结构都返回一个值(其中很多返回NIL)。由于函数中最后计算的表达式是返回值,因此不需要return语句。
回答
表达式是返回值的东西,而语句则不。
举些例子:
1 + 2 * 4 * foo.bar() //Expression foo.voidFunc(1); //Statement
两者之间的大问题是可以将表达式链接在一起,而不能将语句链接在一起。
回答
简单地说:一个表达式求值,一个语句则不。
回答
我们可以在Wikipedia上找到它,但是表达式的计算结果为某个值,而语句没有计算值。
因此,表达式可以在语句中使用,但不能相反。
请注意,某些语言(例如Lisp和我相信Ruby,还有许多其他语言)不能区分语句和表达式...在此类语言中,所有内容都是一种表达式,可以与其他表达式链接。
回答
表达式:可以求值的东西。示例: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
),但几乎可以将任何表达式用作语句(在Conly赋值,调用,递增和递减表达式可以用作语句;请参见Scott Wisniewski的答案)。
具有两个"句法类别"(这是事物陈述和表达的技术名称)可能导致重复工作。例如,C有两种形式的条件语句,即语句形式
if (E) S1; else S2;
和表达形式
E ? E1 : E2
有时人们想要的副本并不存在:例如,在标准C语言中,只有一条语句可以声明新的局部变量,但是此功能足够有用,以至于
GNU C编译器提供了GNU扩展,该扩展使表达式也可以声明局部变量。
其他语言的设计人员不喜欢这种重复,他们很早就看到,如果表达式可以具有副作用和值,那么语句和表达式之间的语法区别并不是那么有用,因此他们摆脱了这种重复。 Haskell,Icon,Lisp和ML都是没有语法语句的语言,它们只有表达式。即使是类结构化的循环和条件形式也被视为表达式,它们具有值,但不是很有趣。
回答
- 表达式是产生值的任何东西: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
回答
我想对上述乔尔的答案做些小的修正。
C不允许将所有表达式都用作语句。特别是,只能将赋值,调用,递增和递减表达式用作语句。
例如,Ccompiler会将以下代码标记为语法错误:
1 + 2;
回答
有关基于表达式的语言的一些注意事项:
最重要的是:一切都返回值
大括号和花括号之间的代码块和表达式定界没有区别,因为所有内容都是表达式。但是,这不会阻止词法作用域:例如,可以为包含定义的表达式以及其中包含的所有语句的表达式定义局部变量。
在基于表达式的语言中,所有内容都返回一个值。起初可能有点奇怪-(FOR i = 1至10 DO(print i))`返回什么?
一些简单的例子:
(1)
返回1
(1 + 1)
返回2
(1 == 1)
返回TRUE
- ((1 == 2)返回FALSE
- ((IF 1 == 1然后10否则5)返回
10
- ((IF 1 == 2然后10 ELSE 5)返回5
一些更复杂的示例:
- 有些事情,例如某些函数调用,实际上并没有返回有意义的值(只会产生副作用的东西吗?)。调用OpenADoor(),FlushTheToilet()或者TwiddleYourThumbs()将返回某种平凡的值,例如OK,Done或者Success。
- 当在一个较大的表达式中对多个未链接的表达式求值时,在较大的表达式中求值的最后一个值将变为较大的表达式的值。以"(FOR i = 1到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
。语言对待语句的倾向往往会引起问题,如果将它们适当地概括起来会更好。
例如,在Cwe中,非常有用的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"的可能性?是的。它所要做的就是要求return后面跟随一个类型为void的表达式。 default(void)的结果将是void的类型,并且传入的函子的格式必须为Func <TValue,void>(等同于Action <TValue>` )。
许多其他答案暗示我们不能像使用表达式那样链接语句,但是我不确定这个想法从何而来。我们可以将出现在语句之后的;
视为二进制中缀运算符,它采用两个类型为'void'的表达式并将它们组合为单个类型为'void'的表达式。
回答
关于表达式与语句在可组合性(可链接性)方面的重要区别的解释,我最喜欢的参考文献是John Backus的Turing奖论文,《编程可以从von Neumann风格中解放出来吗?
命令式语言(Fortran,C,Java等)强调用于结构化程序的语句,并具有表达方式,这是一种事后思考。功能语言强调表达。纯粹的函数式语言具有如此强大的表达式,以至于无法完全消除语句。