我问的是关于c#的问题,但我认为其他大多数语言也是如此。
有人对表达式和语句有一个很好的定义,区别是什么?
我问的是关于c#的问题,但我认为其他大多数语言也是如此。
有人对表达式和语句有一个很好的定义,区别是什么?
Answers:
表达式:可以求值的东西。示例: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#中, call,increment和decrement表达式可以用作语句;请参见Scott Wisniewski的答案)。
具有两个“句法类别”(这是事物陈述和表达的技术名称)可能导致重复工作。例如,C有两种形式的条件语句,即语句形式
if (E) S1; else S2;
和表达形式
E ? E1 : E2
有时人们想要的复制并不存在:例如,在标准C中,只有一条语句可以声明一个新的局部变量,但是这种功能足够有用,以至于GNU C编译器提供了一个GNU扩展,该扩展使表达式可以声明一个局部变量也是如此。
其他语言的设计师不喜欢这种复制,他们很早就发现,如果表达式可以具有副作用和值,那么语句和表达式之间的语法区别就没有那么有用了,因此他们摆脱了这种重复。Haskell,Icon,Lisp和ML都是没有语法语句的语言,它们只有表达式。甚至类结构化的循环和条件形式也被视为表达式,它们具有值-但不是很有趣。
callfunc(x = 2);
传递x
给callfunc
,而不是2
。如果x
为float,callfunc(float)
则将调用,而不是callfunc(int)
。在C ++中,如果您传递x=y
给func
,并func
获取引用并对其进行更改,则它会更改x
而不是y
。
where
haskell中的子句被认为是表达而不是陈述。 learnyouahaskell.com/syntax-in-functions#where
where
实际上是函数声明的一部分,而不是表达式或语句的一部分。
请注意,在C中,“ =”实际上是一个运算符,它执行两件事:
这是ANSI C语法的摘录。您可以看到C没有很多不同类型的语句...程序中的大多数语句是表达式语句,即,末尾带有分号的表达式。
statement
: labeled_statement
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
;
expression_statement
: ';'
| expression ';'
;
表达式是返回值的东西,而语句则不。
举些例子:
1 + 2 * 4 * foo.bar() //Expression
foo.voidFunc(1); //Statement
两者之间的大问题是可以将表达式链接在一起,而不能将语句链接在一起。
foo.voidFunc(1);
是具有空值的表达式。while
和if
是声明。
return
可以认为是子语句。
关于表达式与语句在可组合性(可链接性)方面的重要区别的解释,我最喜欢的参考文献是John Backus的Turing奖论文,可以从von Neumann风格中解放编程吗?。
命令式语言(Fortran,C,Java等)强调用于构造程序的语句,并具有表达方式,这是一种事后思考。功能语言强调表达。 纯粹的函数式语言具有如此强大的表达式,以致于无法完全消除语句。
可以对表达式求值以获得值,而语句不返回值(它们的类型为void)。
当然,函数调用表达式也可以视为语句,但是除非执行环境具有特殊的内置变量来保存返回的值,否则无法检索它。
面向语句的语言要求所有过程都是语句列表。面向表达式的语言(可能是所有功能语言)是表达式列表,或者在LISP的情况下,是一种长S表达式,表示表达式列表。
尽管两种类型都可以组成,但是只要类型匹配,大多数表达式都可以任意组成。每种类型的语句都有自己的方式来组合其他语句,如果它们能做到的话。Foreach和if语句只需要一个语句,或者所有从属语句都一个接一个地放在语句块中,除非这些子语句允许使用自己的子语句。
语句也可以包含表达式,其中表达式实际上并不包含任何语句。但是,有一个例外是表示函数的lambda表达式,因此可以包含函数可以包含的任何内容,除非该语言仅允许有限的lambda,例如Python的单表达式lambda。
在基于表达式的语言中,您所需要的只是一个函数的单个表达式,因为所有控制结构都返回一个值(其中很多返回NIL)。由于函数中最后计算的表达式是返回值,因此不需要return语句。
null
)?会不会void
更像单位类型(但无法访问其单个值)?
null
值是一个真正的pseudovalue表示此引用是指一些不存在的。
简单地说:一个表达式求值,一个语句则不。
{}
是一个声明。把这个词放在吓人的引号中不会改变这一点。语句是具有语义的句法构造。没有“语义层”之类的东西-您似乎是在指执行。您说您想做到准确,但是您失败了。您对“选民的无知”的抱怨纯属偶然。您没有有关降低投票者心理状态的信息。
{}
被定义为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 的示例为例,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
)
语句是表达式的一种特例,带有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);
}
可以在编译器处理的可能性TResult
是void
?是。它所要做的就是要求return后面跟随一个类型为的表达式void
。的结果default(void)
将为类型void
,并且传入的函子将采用以下形式Func<TValue, void>
(相当于Action<TValue>
)。
许多其他答案暗示您不能像使用表达式那样链接语句,但是我不确定这个想法从何而来。我们可以将;
在语句之后出现的作为二进制中缀运算符,将两个type的表达式void
组合成一个type的表达式void
。
声明,
语句是从中构建所有C#程序的过程构建块。语句可以声明局部变量或常量,调用方法,创建对象或为变量,属性或字段分配值。
用花括号括起来的一系列语句构成一个代码块。方法主体是代码块的一个示例。
bool IsPositive(int number)
{
if (number > 0)
{
return true;
}
else
{
return false;
}
}
C#中的语句通常包含表达式。C#中的表达式是包含文字值,简单名称或运算符及其操作数的代码片段。
表达,
表达式是一段代码片段,可以将其评估为单个值,对象,方法或名称空间。两种最简单的表达式类型是文字和简单名称。文字是没有名称的常量值。
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;}
:)
我对这里的任何答案都不满意。我看了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
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
+
,-
,*
,/
,&
,|
,&&
,||
,...)throw
子句也是一个表达式陈述是语法上完整的句子。表达式不是。例如
x = 5
读为“ x得到5”。这是一个完整的句子。编码
(x + 5)/9.0
读为“ x加5均除以9.0”。这不是完整的句子。该声明
while k < 10:
print k
k += 1
是完整的句子。注意,循环头不是。“而k <10时”是从句。
while
具有身体的A 仍然是Scala中的一种表达。如果它产生副作用,也可能是一条语句,我对此很不满意的答案允许这样做(一个表达式也可以是一条语句)。我的答案是唯一正确的答案。抱歉,所有无法理解的读者。
(x + 5)/9.0
绝对可以单独声明。同样,如果在语法上完整地表示您是一个有效程序,则C不允许语句作为单个程序独立存在。
这是我找到的最简单答案之一的总结。
最初由Anders Kaseorg回答
语句是执行某些操作的完整代码行,而表达式是代码中可求值的任何部分。
可以使用运算符将表达式“水平”组合为较大的表达式,而只能通过逐个编写或使用块构造来“垂直”组合语句。
每个表达式都可以用作语句(其作用是评估表达式并忽略结果值),但是大多数语句不能用作表达式。
这些概念的事实基础是:
表达式:一个语法类别,可以将其实例评估为一个值。
声明:一种句法类别,其实例可能涉及表达式的求值,并且不能保证求值的结果值(如果有)。
除了在几十年前的FORTRAN最初的上下文之外,公认的答案中的表达式和语句定义显然都是错误的:
sizeof
永远不会评估用作运算符的操作数的表达式。(顺便说一句,我想在有关C的材料的答案中添加[需要引用],因为我不记得DMR是否有这样的意见。似乎没有,否则,没有理由在C的设计中保留功能重复项:尤其是逗号运算符与语句。)
(以下理由并不是对原始问题的直接回答,但我认为有必要澄清此处已回答的问题。)
但是,是否有必要在通用编程语言中使用特定类别的“语句”是令人怀疑的:
begin
,Scheme中)或单子结构的语法糖代替。++i + ++i
C语言无意义的观点。)那么为什么要声明呢?无论如何,历史已经是一团糟。似乎大多数语言设计师都不会谨慎选择。
更糟糕的是,它甚至给某些类型系统爱好者(他们对PL历史不太熟悉)带来了一些误解,认为类型系统必须与操作语义规则的更基本设计有关,这与重要的事情有关。
认真地说,在许多情况下,根据类型进行推理并没有那么糟糕,但是在这种特殊情况下尤其没有建设性。甚至专家也可以搞砸。
例如,有人强调将打字性作为反对无界延续的传统方法的中心论点。尽管结论在某种程度上是合理的,并且对组合函数的见解还可以(但对本质而言仍然太幼稚),但是这种说法并不合理,因为它在实践中完全忽略了“旁通道”方法,例如_Noreturn any_of_returnable_types
(在C11中)编码Falsum
。 严格来讲,状态无法预测的抽象机与“崩溃的计算机”并不相同。
在面向语句的编程语言中,代码块被定义为语句列表。换句话说,语句是一种语法,您可以将其放入代码块中而不会引起语法错误。
维基百科类似地定义了单词陈述
在计算机编程中,语句是命令式编程语言的语法单元,表示要执行的某些操作。用这种语言编写的程序是由一个或多个语句序列构成的
注意后面的陈述。(尽管在这种情况下,“程序”在技术上是错误的,因为C和Java都会拒绝不包含任何语句的程序。)
维基百科将单词表达定义为
编程语言中的表达式是一种语法实体,可以对其进行评估以确定其值
但是,这是错误的,因为在Kotlin中,它throw new Exception("")
是一个表达式,但是在被求值时,它只是抛出异常,而从不返回任何值。
在静态类型的编程语言中,每个表达式都有一个类型。但是,此定义不适用于动态类型的编程语言。
就个人而言,我将表达式定义为一种语法,可以与运算符或函数调用组合起来以产生更大的表达式。这实际上类似于维基百科对表达的解释:
它是编程语言解释(根据其特定的优先级和关联规则)并计算以产生(在有状态环境中“返回”)另一个值的一个或多个常量,变量,函数和运算符的组合
但是,问题出在C编程语言中,给定一个函数executeSo,如下所示:
void executeSomething(void){
return;
}
是executeSomething()
表达还是陈述?根据我的定义,这是一条语句,因为按照Microsoft的C参考语法中的定义,
您不能以任何方式使用类型为void的表达式的(不存在)值,也不能将void表达式(通过隐式或显式转换)转换为除void外的任何类型
但是同一页清楚地表明这种语法是一种表达。
为了改进和验证我的先前答案,应在适用时从计算机科学类型理论中解释编程语言术语的定义。
表达式的类型不同于Bottom类型,即它具有一个值。语句具有单位或底部类型。
由此可见,语句只能在产生副作用时在程序中起作用,因为它要么不能返回值,要么只能返回不可赋值的Unit类型的值(在某些语言中,例如可以将C void
或(例如在Scala中)存储起来,以便对该语句进行延迟评估。
显然,a @pragma
或a /*comment*/
没有类型,因此与语句有所区别。因此,唯一没有副作用的语句类型将是非操作。非手术仅用作未来副作用的占位符。由于声明而采取的任何其他措施都是副作用。同样,编译器提示(例如@pragma
)不是语句,因为它没有类型。
@pragma
或/*comment*/
不一致。
最精确的说,一个语句必须具有“副作用”(即必须),而表达式必须具有一个值类型(即非底部类型)。
语句的类型是单位类型,但是由于暂停定理,单位是虚构的,所以可以说底部类型。
Void
并非完全是底部类型(不是所有可能类型的子类型)。它以没有完全声音类型系统的语言存在。这听起来像一个势利的声明,但是完整性,例如方差注释类的对于编写可扩展软件至关重要。
让我们看看维基百科在这个问题上要说些什么。
https://zh.wikipedia.org/wiki/Statement_(computer_science)
在计算机程序设计中,语句是命令式编程语言中最小的独立元素,它表示要执行的某些操作。
许多语言(例如C)在语句和定义之间进行区分,其中语句仅包含可执行代码,而定义则声明标识符,而表达式仅求值。
pass
是一条语句。这是无人值守的,它不会评估任何东西。