表达与陈述


431

我问的是关于c#的问题,但我认为其他大多数语言也是如此。

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


4
我发现您选择的答案是模棱两可的。表达式还可以执行某些操作,它可以求值。我提供了一个明确的答案。
谢尔比·摩尔三世

4
@ShelbyMooreIII-明确且错误。可接受的答案以非正式的方式措词,但该措词易于理解-最重要的是,它传达的含义是准确的。
贾斯汀·摩根

@JustinMorgan不幸的是,对于大多数现代语言,包括C样语言,公认的答案中的定义也显然是错误的(“求值” /“一行代码”):表达式可以在未求值的上下文中使用,而语句则一无所有与线条有关。即使有一些解释,简短的答案也是令人困惑和误导的。
FrankHB

Answers:


520

表达式:可以求值的东西。示例: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都是没有语法语句的语言,它们只有表达式。甚至类结构化的循环和条件形式也被视为表达式,它们具有值-但不是很有趣。


9
如果我在这里不是对您的误解,您似乎声称“(setf(third foo)'goose)”是一个表达式,而不是一个语句,都是因为它是Lisp,“没有语句”,以及因为Lisp比C语言早十多年,而C语言是“模糊[表达式和语句之间]界线的最早的流行语言”。能给我解释一下细节吗?
cjs

2
@Curt Sampson,您是否曾提出过另外一个问题?
凯利·法国

5
如果我没记错的话,callfunc(x = 2);传递xcallfunc,而不是2。如果x为float,callfunc(float)则将调用,而不是callfunc(int)。在C ++中,如果您传递x=yfunc,并func获取引用并对其进行更改,则它会更改x而不是y
加百利

在上面的答案中,写道:“ Haskell,...是所有不具有语法语句的语言-它们仅具有表达式”。我很好奇为什么wherehaskell中的子句被认为是表达而不是陈述。 learnyouahaskell.com/syntax-in-functions#where
skgbanga

我相信@skgbanga where实际上是函数声明的一部分,而不是表达式或语句的一部分。
Akangka

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


2
关于语句的逻辑不正确。声明性程序也可以执行,但是声明性程序没有语句。陈述是并且确实是“副作用”,即必须。cf. 我的回答
谢尔比·摩尔三世

15

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

举些例子:

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

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


6
可以将陈述链接起来。{stmt1; stmt2; stmt3;}是一条链,它本身也是一个(compound)语句。
休·艾伦

5
foo.voidFunc(1);是具有空值的表达式。whileif是声明。
tzot

我对不链接语句感到好奇。是否会出现“如果(x> 1)返回;” 被认为将两个语句链接在一起?
西蒙·图西

1
@SimonTewsi我相信return可以认为是子语句。
RastaJedi

@SimonTewsi这里的return语句隐含在if语句的块内,因此它是if语句的一部分,而不是与其链接。编译器允许我们在这里省略花括号,因为它是单个行块。
user2597608

9

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

因此,表达式可以在语句中使用,但不能反过来使用。

请注意,某些语言(例如Lisp和我相信Ruby,还有许多其他语言)不能区分语句和表达式...在此类语言中,所有内容都是一种表达式,可以与其他表达式链接。


8

关于表达式与语句在可组合性(可链接性)方面的重要区别的解释,我最喜欢的参考文献是John Backus的Turing奖论文,可以从von Neumann风格中解放编程吗?

命令式语言(Fortran,C,Java等)强调用于构造程序的语句,并具有表达方式,这是一种事后思考。功能语言强调表达。 纯粹的函数式语言具有如此强大的表达式,以致于无法完全消除语句。


5

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

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

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

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

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

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


语句的类型是底部类型。Void不是底部类型。看我的回答
谢尔比·摩尔三世

1
是不是null类型底部类型(单值null)?会不会void更像单位类型(但无法访问其单个值)?
马克·西达德

如果void是永不返回的函数的返回类型(例如,throw发生错误的函数),则它是底部类型。否则void单位类型。您正确的说,一个不能分开的语句具有单位类型。但是,可能会有所不同的是底部类型。由于停止定理,我们通常不能证明函数不会发散,所以我认为单位是虚构的。底部类型不能具有值,因此不能具有单个值null
谢尔比·摩尔三世

1
关于我三年前说过的话,我不知道我是否仍然认为语句实际上是无效类型或任何类型。在我熟悉的基于语句的语言中,只有和任何存储或返回值的内容(例如,表达式,变量,成员和函数)都可以具有类型。我通常将底部类型视为空集(无值),因此本体论上不存在的任何内容都将具有此类型。一个null值是一个真正的pseudovalue表示此引用是指一些不存在的。
马克·

1
马克·我感谢您的回应的合理性。您基本上是从我的嘴里说出了这些话,希望我很清楚地向您承认您提高单位点数是正确的。我认为我们同意。我不想麻烦提到这一点,但似乎有些人认为我很消极。我只是想成为事实。
谢尔比·摩尔三世

4

简单地说:一个表达式求值,一个语句则不。


那么,声明有什么作用?没有?
谢尔比·摩尔三世

1
它可以做一些事情,但没有任何效果。即,您不能将其结果分配给变量,而可以使用表达式。
马修·辛克尔

因此,正如我极不赞成的回答所说,声明必须具有副作用。语句可能还有什么其他用途?即使NO-OP被认为是一个语句(语法上只是“语句”,而在语义层上不是,因为在解析后将其删除,而语义是我们在此处讨论的内容),它也不会解释声明的一般用途是。
谢尔比·摩尔三世

1
@ShelbyMooreIII语句不需要执行任何操作或没有副作用。例如,{}是一个声明。把这个词放在吓人的引号中不会改变这一点。语句是具有语义的句法构造。没有“语义层”之类的东西-您似乎是在指执行。您说您想做到准确,但是您失败了。您对“选民的无知”的抱怨纯属偶然。您没有有关降低投票者心理状态的信息。
吉姆·巴尔特

是的,除了理智上的不诚实之外,每个人都是错的。{}被定义为C#语言规范中的一条语句。
吉姆·巴尔特

4

有关基于表达式的语言的一些注意事项:


最重要的是:一切都返回值


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


在基于表达式的语言中,所有内容都返回一个值。一开始可能有点奇怪- (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
)

2

语句是表达式的一种特例,带有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<TValue, void>(相当于Action<TValue>)。

许多其他答案暗示您不能像使用表达式那样链接语句,但是我不确定这个想法从何而来。我们可以将;在语句之后出现的作为二进制中缀运算符,将两个type的表达式void组合成一个type的表达式void


声明不是表达的特殊情况。在某些语言(即大多数C后继语言)中,实际上是相反的。
Akangka

2

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

语句基本上就像步骤或算法中的指令一样,语句执行的结果是指令指针的实现(在汇编器中称为)

表达式并不表示和执行顺序乍一看,它们的目的是求值并返回值。在命令式编程语言中,对表达式的求值具有顺序,但这仅是由于命令式模型的缘故,但这并不是其本质。

声明示例:

for
goto
return
if

(所有这些都暗示着执行行(语句)前进到另一行)

表达式示例:

2+2

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


副作用呢?
奥斯汀·亨利

@AustinHenley没有要求。实际上,表达肯定会产生副作用。
Akangka

1

声明

语句是从中构建所有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;}:)
Mahdi Tahsildari

1

我更喜欢 statement形容词在形式逻辑上。它改变了计算中一个或多个变量的状态,从而使对它们的值的判断是正确的还是错误的。

我猜想,当引入新的术语或单词,“重新定义”现有单词或用户不了解它们所描述的现有,已建立或“正确”术语时,整个计算世界和科学界总会有混乱的感觉。


1

我对这里的任何答案都不满意。我看了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 ++中被视为语句的其他概念。

  • expression-statement s是不言自明的(语句可以由零或多个表达式,请仔细阅读语法,这很棘手)
  • case 例如是 标签的陈述
  • 选择陈述if if/elsecase
  • 迭代语句 s为whiledo...whilefor (...)
  • 跳转语句 s为breakcontinuereturn(可返回式),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-expression-嗯?该throw子句也是一个表达式

0

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

x = 5

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

(x + 5)/9.0

读为“ x加5均除以9.0”。这不是完整的句子。该声明

while k < 10: 
    print k
    k += 1

是完整的句子。注意,循环头不是。“而k <10时”是从句。


while是一种表达,是某些语言(例如Scala)的表达。您正在将语法与输入混淆。看我的回答
谢尔比·摩尔三世

这是scala中的while循环:tutorialspoint.com/scala/scala_while_loop.htm 具有谓词且没有主体的循环不是语法上完整的句子。这不是完整的表达。您需要身体来完成它的表达。
ncmathsadist

while具有身体的A 仍然是Scala中的一种表达。如果它产生副作用,也可能是一条语句,我对此很不满意的答案允许这样做(一个表达式也可以是一条语句)。我的答案是唯一正确的答案。抱歉,所有无法理解的读者。
谢尔比·摩尔

语法完整是什么意思?在C中,(x + 5)/9.0绝对可以单独声明。同样,如果在语法上完整地表示您是一个有效程序,则C不允许语句作为单个程序独立存在。
Akangka

语法上完整的:构成完整的句子。
ncmathsadist

0

这是我找到的最简单答案之一的总结。

最初由Anders Kaseorg回答

语句是执行某些操作的完整代码行,而表达式是代码中可求值的任何部分。

可以使用运算符将​​表达式“水平”组合为较大的表达式,而只能通过逐个编写或使用块构造来“垂直”组合语句。

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

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


0

这些概念的事实基础是:

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

声明:一种句法类别,其实例可能涉及表达式的求值,并且不能保证求值的结果值(如果有)。

除了在几十年前的FORTRAN最初的上下文之外,公认的答案中的表达式和语句定义显然都是错误的:

  • 表达式可以是未赋值的操作数。永远不会产生价值。
    • 非严格评估中的子表达式可以绝对未评估。
      • 大多数类似C的语言都有所谓的短路评估规则,可以有条件地跳过某些子表达式评估,尽管有副作用也不会改变最终结果。
    • C和某些类似C的语言具有未评估的操作数的概念,甚至可以在语言规范中对其进行规范地定义。此类构造用于绝对避免评估,因此可以静态区分其余的上下文信息(例如类型或对齐要求),而无需在程序转换后更改行为。
      • 例如,sizeof永远不会评估用作运算符的操作数的表达式。
  • 语句与行构造无关。根据语言规范,他们可以做的不仅仅是表达。
    • 作为旧式FORTRAN的直接后代,现代Fortran具有可执行语句 s和可执行语句 s的概念。
    • 同样,C ++将声明定义为翻译单元的顶级子类别。C ++中的声明是声明。(在C中不是这样。)还有诸如Fortran的可执行语句之类的表达式语句。
    • 为了与表达式进行比较,只有“可执行”语句才重要。但是您不能忽略这样一个事实,即语句已经被普遍化为构成这种命令性语言的翻译单元的结构。因此,如您所见,类别的定义相差很大。这些语言之间(可能)唯一保留的共同属性是,语句应按词法顺序(对于大多数用户,从左到右和从上到下)进行解释。

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

(以下理由并不是对原始问题的直接回答,但我认为有必要澄清此处已回答的问题。)

但是,是否有必要在通用编程语言中使用特定类别的“语句”是令人怀疑的:

  • 在通常的设计中,不保证语句比表达式具有更多的语义功能。
    • 许多语言已经成功地放弃了语句的概念,以获得简洁,整洁和一致的总体设计。
      • 在这种语言中,表达式可以执行所有旧式语句可以做的一切:通过对表达式进行显式未指定(例如,在R n RS Scheme中)或具有特殊值(例如,不能从正则表达式评估中得出。
      • 评估表达式的词汇顺序规则可以由显式序列控制算子(例如begin,Scheme中)或单子结构的语法糖代替。
      • 可以将其他类型的“语句”的词汇顺序规则作为语法扩展(例如,使用卫生宏)导出,以获得相似的语法功能。(它实际上可以做更多。)
    • 相反,陈述不能具有这样的常规规则,因为它们不构成评估:只有这样一种“子陈述评估”的通用概念。(即使有的话,我也怀疑除了从现有的表达式求值规则中复制和粘贴之外,还有更多其他事情。)
      • 通常,保留语句的语言也将具有用于表达计算的表达式,并且该语句有一个顶级子类别,保留给该子类别的表达式求值。例如,C ++将所谓的表达式语句作为子类别,并使用舍弃值表达式评估规则来指定在这种情况下进行全表达式评估的一般情况。诸如C#之类的某些语言选择优化上下文以简化用例,但这样会使规范更加膨胀。
  • 对于编程语言的用户而言,语句的重要性可能会使它们进一步混淆。
    • 语言中表达规则和陈述规则的分离要求学习一种语言付出更多的努力。
    • 天真的词汇顺序解释隐藏了更重要的概念:表达评估。(这可能是最有问题的。)
      • 即使语句中的完整表达式的求值也受到词法顺序的约束,子表达式也不是(必需的)约束。除了与语句相关的任何规则外,用户最终还应该学习这一点。(考虑如何使新手掌握++i + ++iC语言无意义的观点。)
      • 诸如Java和C#之类的某些语言进一步限制了子表达式的求值顺序,以允许忽略求值规则。甚至可能会有更多问题。
        • 对于已经学习表达评估思想的用户来说,这似乎过分具体。它还鼓励用户社区遵循语言设计的模糊思维模型。
        • 它使语言规范更加膨胀。
        • 在引入更复杂的原语之前,它会错过评估中不确定性的表达,这对优化很有害。
      • 像C ++(尤其是C ++ 17)之类的几种语言指定了评估规则的更微妙的上下文,以解决上述问题。
        • 它使语言规范大量膨胀。
        • 这完全不利于普通用户...

那么为什么要声明呢?无论如何,历史已经是一团糟。似乎大多数语言设计师都不会谨慎选择。

更糟糕的是,它甚至给某些类型系统爱好者(他们对PL历史不太熟悉)带来了一些误解,认为类型系统必须与操作语义规则的更基本设计有关,这与重要的事情有关。

认真地说,在许多情况下,根据类型进行推理并没有那么糟糕,但是在这种特殊情况下尤其没有建设性。甚至专家也可以搞砸。

例如,有人强调将打字性作为反对无界延续的传统方法的中心论点。尽管结论在某种程度上是合理的,并且对组合函数的见解还可以(但对本质而言仍然太幼稚),但是这种说法并不合理,因为它在实践中完全忽略了“旁通道”方法,例如_Noreturn any_of_returnable_types(在C11中)编码Falsum严格来讲,状态无法预测的抽象机与“崩溃的计算机”并不相同。


0

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

维基百科类似地定义了单词陈述

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

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

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

编程语言中的表达式是一种语法实体,可以对其进行评估以确定其值

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

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

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

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

但是,问题出在C编程语言中,给定一个函数executeSo,如下所示:

void executeSomething(void){
    return;
}

executeSomething()表达还是陈述?根据我的定义,这是一条语句,因为按照Microsoft的C参考语法中的定义,

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

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


-6

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

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

由此可见,语句只能在产生副作用时在程序中起作用,因为它要么不能返回值,要么只能返回不可赋值的Unit类型的值(在某些语言中,例如可以将C void或(例如在Scala中)存储起来,以便对该语句进行延迟评估。

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


2
这种区别与表达式的类型无关。语法定义的语句在许多语言中根本没有类型。尽管从理论上讲,我不反对为这种类型分配类型,但是在逻辑上不一致@pragma/*comment*/不一致。
FrankHB

-11

最精确的说,一个语句必须具有“副作用”(即必须),而表达式必须具有一个类型(即非底部类型)。

语句类型是单位类型,但是由于暂停定理,单位是虚构的,所以可以说底部类型


Void并非完全是底部类型(不是所有可能类型的子类型)。它以没有完全声音类型系统的语言存在。这听起来像一个势利的声明,但是完整性,例如方差注释类的对于编写可扩展软件至关重要。

让我们看看维基百科在这个问题上要说些什么。

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

在计算机程序设计中,语句是命令式编程语言中最小的独立元素,它表示要执行的某些操作

许多语言(例如C)在语句和定义之间进行区分,其中语句仅包含可执行代码,而定义则声明标识符,而表达式仅求值。


5
声明不必有副作用。例如,在python中pass是一条语句。这是无人值守的,它不会评估任何东西。
马修·申克尔

9
-1这是错误的。声明不必具有副作用。请参见C#语言规范的 1.5节。它不仅没有指定语句必须具有副作用,而且还列出了几个没有副作用的语句。
NullUserException 2011年

2
@NullUserException我读了那部分。声明,表达式,选择,迭代和跳转语句都可能产生副作用。但是,如果有RT表达式,则它不是语句。我意识到规范将表达式与语句等同起来,但是这个问题要求它们之间有区别。因此,要么问题没有答案,要么您从字面上考虑了C#规范。票数最高的答案是试图说我做了什么。但是“做某事”是没有意义的。“副作用”是说“做某事”的有意义的方式。我们必须思考,而不仅仅是反省。
谢尔比·摩尔三世

13
@ShelbyMooreIII你是对的。官方文件是错误的。马克·格雷韦尔(Marc Gravell)和乔恩·斯基特(Jon Skeet)可能是在埃里克·利珀特(Eric Lippert)之外最受人尊敬的C#张贴者,这是错误的。我和其他所有对你投反对票并留下评论以解释我们立场的人都是错误的。你是对的。显然,您是唯一知道他们在说什么的人,因为您比SO的其余部分都聪明得多。
NullUserException 2011年

3
知道诸如语句,表达式,强制转换,转换等概念的精确定义,对99.999%的日常编程任务的影响为零。想想那个。另外:大多数人都不在乎Haskell和Scala。
NullUserException 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.