我问的是 c#,但我认为它在大多数其他语言中都是一样的。
有没有人对表达式和语句有一个很好的定义,有什么区别?
表达式:求值的东西。示例:1+2/x 语句:执行某事的一行代码。示例:转到 100
在最早的通用编程语言(如 FORTRAN)中,区别非常明显。在 FORTRAN 中,语句是一个执行单元,是您所做的事情。它不被称为“线”的唯一原因是因为有时它跨越多条线。一个表达式本身不能做任何事情......你必须将它分配给一个变量。
1 + 2 / X
在 FORTRAN 中是一个错误,因为它什么都不做。你必须用这个表达式做点什么:
X = 1 + 2 / X
FORTRAN 没有我们今天所知道的语法——这个想法是与巴库斯-瑙尔形式 (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#只有赋值、调用、递增和递减表达式可以用作语句;参见 Scott Wisniewski's answer)。
拥有两个“句法类别”(这是事物陈述和表达式的技术名称)可能会导致重复工作。例如,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
表达式是返回值的东西,而语句则不。
举些例子:
1 + 2 * 4 * foo.bar() //Expression
foo.voidFunc(1); //Statement
两者之间的大交易是您可以将表达式链接在一起,而不能将语句链接在一起。
foo.voidFunc(1);
是具有 void 值的表达式。 while
和 if
是语句。
return
被认为是一个子语句。
您可以在 wikipedia 上找到它,但表达式被评估为某个值,而语句没有评估值。
因此,表达式可以在语句中使用,但反之则不行。
请注意,某些语言(例如 Lisp,我相信 Ruby 和许多其他语言)不区分语句与表达式……在此类语言中,一切都是表达式,并且可以与其他表达式链接。
为了解释表达式与语句的可组合性(可链接性)的重要差异,我最喜欢的参考资料是 John Backus 的图灵奖论文,Can programming be liberated from the von Neumann style?。
命令式语言(Fortran、C、Java 等)强调构建程序的语句,并将表达式作为一种事后的想法。函数式语言强调表达式。纯函数式语言具有如此强大的表达式,以至于可以完全消除语句。
可以对表达式求值以获取值,而语句不返回值(它们是 void 类型)。
函数调用表达式当然也可以被认为是语句,但是除非执行环境有一个特殊的内置变量来保存返回值,否则没有办法检索它。
面向语句的语言要求所有过程都是语句的列表。面向表达式的语言(可能都是函数式语言)是表达式列表,或者在 LISP 的情况下,是一个表示表达式列表的长 S 表达式。
尽管这两种类型都可以组合,但只要类型匹配,大多数表达式都可以任意组合。每种类型的语句都有自己的方式来组成其他语句,如果他们能做到的话。 Foreach 和 if 语句需要单个语句或所有从属语句一个接一个地进入语句块,除非子语句允许它们自己的子语句。
语句还可以包含表达式,其中表达式实际上并不包含任何语句。但是,一个例外是 lambda 表达式,它表示一个函数,因此可以包含函数可以包含的任何内容,除非该语言仅允许有限的 lambda,例如 Python 的单表达式 lambda。
在基于表达式的语言中,您只需要一个函数表达式,因为所有控制结构都返回一个值(其中很多都返回 NIL)。不需要 return 语句,因为函数中最后计算的表达式是返回值。
Void
不是底部类型。请参阅my answer。
null
的单个值)吗? void
会不会更像 unit 类型(但它的单个值不可访问)?
void
是从不返回的函数的返回类型(例如,throw
是错误的函数),则它是 bottom type。否则 void
是 unit type。您是正确的,不能发散的语句具有单位类型。但是可以发散的陈述是底部类型。由于停止定理,我们通常无法证明函数不发散,所以我认为单位是虚构的。底部类型不能有值,因此它不能有单个值 null
。
null
值实际上是一个伪值,表示引用引用了不存在的东西。
简单地说:表达式计算为一个值,而语句则没有。
{}
是一个语句。把这个词放在吓人的引号中并不会改变这一点。语句是具有语义的句法结构。没有“语义层”之类的东西——您似乎指的是执行。你说你试图准确,但你没有做到这一点。您对“选民的无知”的抱怨纯属自欺欺人;您没有关于投票者的心理状态的信息。
{}
被定义为 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 循环的值为“10”,它导致 (print i) 表达式被计算 10 次,每次返回 i 作为细绳。最后一次通过返回 10,我们的最终答案
通常需要稍微改变思维方式才能充分利用基于表达式的语言,因为一切都是表达式这一事实使得“内联”很多东西成为可能
举个简单的例子:
FOR i = 1 to (IF MyString == "Hello, World!" THEN 10 ELSE 5) DO (LotsOfCode)
是非基于表达式的完美替代品
IF MyString == "你好,世界!" 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
)
我对这里的任何答案都不满意。我查看了 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/else, case
迭代语句是 while、do...while、for (...)
跳转语句是 break、continue、return(可以返回表达式)、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 子句也是一个表达式
语句是表达式的一种特殊情况,具有 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 需要采用 Func<TValue, void>
形式(相当于 Action<TValue>
)。
许多其他答案暗示您不能像使用表达式那样将语句链接起来,但我不确定这个想法来自哪里。我们可以将出现在语句之后的 ;
视为二元中缀运算符,它采用两个 void
类型的表达式并将它们组合成一个 void
类型的表达式。
语句 -> 顺序遵循的说明 表达式 -> 返回值的求值
语句基本上就像算法中的步骤或指令,语句执行的结果是指令指针的实现(汇编中所谓的)
表达式乍一看并不暗示和执行顺序,它们的目的是评估和返回一个值。在命令式编程语言中,表达式的求值是有顺序的,但这只是因为命令式模型,而不是它们的本质。
语句示例:
for
goto
return
if
(所有这些都意味着将执行行(语句)推进到另一行)
表达式示例:
2+2
(这并不意味着执行的想法,而是评估的想法)
这些概念的事实基础是:
表达式:一个语法类别,其实例可以评估为一个值。
语句:一个语法类别,其实例可能涉及表达式的评估,并且评估的结果值(如果有)不保证可用。
除了早期几十年 FORTRAN 的最初背景之外,公认答案中的表达式和语句的定义显然都是错误的:
表达式可以是未赋值的操作数。价值永远不会从它们中产生。非严格求值中的子表达式绝对可以不求值。大多数类 C 语言都有所谓的短路评估规则,可以有条件地跳过一些子表达式评估,尽管有副作用,但不会改变最终结果。 C 和一些类 C 语言具有未计算操作数的概念,甚至可以在语言规范中进行规范定义。这样的构造用于明确地避免评估,因此可以在不改变程序翻译后的行为的情况下静态区分剩余的上下文信息(例如类型或对齐要求)。例如,用作 sizeof 运算符的操作数的表达式永远不会被计算。
非严格求值中的子表达式绝对可以不求值。大多数类 C 语言都有所谓的短路评估规则,可以有条件地跳过一些子表达式评估,尽管有副作用,但不会改变最终结果。
大多数类 C 语言都有所谓的短路评估规则,可以有条件地跳过一些子表达式评估,尽管有副作用,但不会改变最终结果。
C 和一些类 C 语言具有未计算操作数的概念,甚至可以在语言规范中进行规范定义。这样的构造用于明确地避免评估,因此可以在不改变程序翻译后的行为的情况下静态区分剩余的上下文信息(例如类型或对齐要求)。例如,用作 sizeof 运算符的操作数的表达式永远不会被计算。
例如,用作 sizeof 运算符的操作数的表达式永远不会被计算。
语句与行结构无关。根据语言规范,它们可以做的不仅仅是表达式。现代 Fortran 作为旧 FORTRAN 的直系后代,具有可执行语句和不可执行语句的概念。同样,C++ 将声明定义为翻译单元的顶级子类别。 C++ 中的声明是一个语句。 (在 C 中不是这样。)还有表达式语句,如 Fortran 的可执行语句。为了与表达式进行比较,只有“可执行”语句很重要。但是你不能忽略这样一个事实,即语句已经被概括为构成这种命令式语言的翻译单元的构造。因此,如您所见,该类别的定义差异很大。这些语言中(可能)唯一保留的共同属性是语句应按词汇顺序(对于大多数用户,从左到右和从上到下)进行解释。
现代 Fortran 作为旧 FORTRAN 的直系后代,具有可执行语句和不可执行语句的概念。
同样,C++ 将声明定义为翻译单元的顶级子类别。 C++ 中的声明是一个语句。 (在 C 中不是这样。)还有表达式语句,如 Fortran 的可执行语句。
为了与表达式进行比较,只有“可执行”语句很重要。但是你不能忽略这样一个事实,即语句已经被概括为构成这种命令式语言的翻译单元的构造。因此,如您所见,该类别的定义差异很大。这些语言中(可能)唯一保留的共同属性是语句应按词汇顺序(对于大多数用户,从左到右和从上到下)进行解释。
(顺便说一句,我想在关于 C 的材料的答案中添加[需要引用],因为我不记得 DMR 是否有这样的意见。似乎没有,否则应该没有理由在 C 的设计中保留功能重复:值得注意的是,逗号运算符与语句。)
(以下理由不是对原始问题的直接回应,但我觉得有必要澄清这里已经回答的问题。)
然而,我们是否需要通用编程语言中的特定类别的“语句”是值得怀疑的:
在通常的设计中,不能保证语句比表达式具有更多的语义能力。许多语言已经成功地放弃了语句的概念,以获得干净、整洁和一致的整体设计。在这样的语言中,表达式可以做旧式语句可以做的所有事情:在计算表达式时删除未使用的结果,要么明确地不指定结果(例如在 RnRS 方案中),要么具有特殊值(作为一个单位类型)不能从正常的表达式评估中产生。表达式求值的词汇顺序规则可以被显式的序列控制操作符(例如,在 Scheme 中的 begin)或单子结构的语法糖代替。其他类型的“语句”的词汇顺序规则可以作为句法扩展(例如使用卫生宏)来获得类似的句法功能。 (而且它实际上可以做得更多。)相反,语句不能有这样的常规规则,因为它们不构成评估:没有“子语句评估”这样的常见概念。 (即使有,我怀疑除了从现有的表达式求值规则中复制和粘贴之外,还有更多的东西。)通常,语言保留语句也会有表达式来表达计算,并且语句有一个顶级子类别保留到该子类别的表达式评估。例如,C++有所谓的表达式语句作为子类别,并使用丢弃值表达式评估规则来指定这种上下文中全表达式评估的一般情况。某些语言(如 C#)选择细化上下文以简化用例,但它使规范更加膨胀。
许多语言已经成功地放弃了语句的概念,以获得干净、整洁和一致的整体设计。在这样的语言中,表达式可以做旧式语句可以做的所有事情:在计算表达式时删除未使用的结果,要么明确地不指定结果(例如在 RnRS 方案中),要么具有特殊值(作为一个单位类型)不能从正常的表达式评估中产生。表达式求值的词汇顺序规则可以被显式的序列控制操作符(例如,在 Scheme 中的 begin)或单子结构的语法糖代替。其他类型的“语句”的词汇顺序规则可以作为句法扩展(例如使用卫生宏)来获得类似的句法功能。 (它实际上可以做得更多。)
在这样的语言中,表达式可以做旧式语句可以做的所有事情:在计算表达式时删除未使用的结果,要么明确地不指定结果(例如在 RnRS 方案中),要么具有特殊值(作为一个单位类型)不能从正常的表达式评估中产生。
表达式求值的词汇顺序规则可以被显式的序列控制操作符(例如,在 Scheme 中的 begin)或单子结构的语法糖代替。
其他类型的“语句”的词汇顺序规则可以作为句法扩展(例如使用卫生宏)来获得类似的句法功能。 (它实际上可以做得更多。)
相反,语句不能有这样的常规规则,因为它们不构成评估:只是没有“子语句评估”这样的常见概念。 (即使有,我怀疑除了从现有的表达式求值规则中复制和粘贴之外,还有更多的东西。)通常,语言保留语句也会有表达式来表达计算,并且语句有一个顶级子类别保留到该子类别的表达式评估。例如,C++有所谓的表达式语句作为子类别,并使用丢弃值表达式评估规则来指定这种上下文中全表达式评估的一般情况。某些语言(如 C#)选择细化上下文以简化用例,但它使规范更加膨胀。
通常,语言保留语句也将具有表达计算的表达式,并且有一个顶级子类别的语句保留用于该子类别的表达式评估。例如,C++有所谓的表达式语句作为子类别,并使用丢弃值表达式评估规则来指定这种上下文中全表达式评估的一般情况。某些语言(如 C#)选择细化上下文以简化用例,但它使规范更加膨胀。
对于编程语言的用户来说,语句的重要性可能会使他们更加困惑。语言中表达式和语句规则的分离需要更多的努力来学习一门语言。朴素的词序解释隐藏了更重要的概念:表达式求值。 (这可能是最有问题的。)即使语句中完整表达式的评估受词法顺序的约束,子表达式也不是(必然)。除了与语句相关的任何规则外,用户最终应该了解这一点。 (考虑如何让新手明白 ++i + ++i 在 C 中没有意义。)Java 和 C# 等一些语言进一步限制子表达式的求值顺序,以允许忽略求值规则。问题可能更大。对于已经了解表达式评估概念的用户来说,这似乎是过度指定的。它还鼓励用户社区遵循语言设计的模糊心理模型。它使语言规范更加膨胀。在引入更复杂的原语之前,由于缺少评估的不确定性的表达性,这对优化是有害的。像 C++(特别是 C++17)这样的一些语言指定了更微妙的评估规则上下文,作为上述问题的折衷方案。它使语言规范膨胀了很多。这完全违背了普通用户的简单性......
语言中表达式和语句规则的分离需要更多的努力来学习一门语言。
朴素的词序解释隐藏了更重要的概念:表达式求值。 (这可能是最有问题的。)即使语句中完整表达式的评估受词法顺序的约束,子表达式也不是(必然)。除了与语句相关的任何规则外,用户最终应该了解这一点。 (考虑如何让新手明白 ++i + ++i 在 C 中没有意义。)Java 和 C# 等一些语言进一步限制子表达式的求值顺序,以允许忽略求值规则。问题可能更大。对于已经了解表达式评估概念的用户来说,这似乎是过度指定的。它还鼓励用户社区遵循语言设计的模糊心理模型。它使语言规范更加膨胀。在引入更复杂的原语之前,由于缺少评估的不确定性的表达性,这对优化是有害的。像 C++(特别是 C++17)这样的一些语言指定了更微妙的评估规则上下文,作为上述问题的折衷方案。它使语言规范膨胀了很多。这完全违背了普通用户的简单性......
即使是语句中完整表达式的求值也受到词法顺序的约束,子表达式则不是(必要的)。除了与语句相关的任何规则外,用户最终应该了解这一点。 (考虑如何让新手明白 ++i + ++i 在 C 中没有意义。)
Java 和 C# 等一些语言进一步限制了子表达式的求值顺序,以允许忽略求值规则。问题可能更大。对于已经了解表达式评估概念的用户来说,这似乎是过度指定的。它还鼓励用户社区遵循语言设计的模糊心理模型。它使语言规范更加膨胀。在引入更复杂的原语之前,由于缺少评估的不确定性的表达性,这对优化是有害的。
对于已经了解表达式评估概念的用户来说,这似乎是过度指定的。它还鼓励用户社区遵循语言设计的模糊心理模型。
它使语言规范更加膨胀。
在引入更复杂的原语之前,由于缺少评估的不确定性的表达性,这对优化是有害的。
像 C++(特别是 C++17)这样的一些语言指定了更微妙的评估规则上下文,作为上述问题的折衷方案。它使语言规范膨胀了很多。这完全违背了普通用户的简单性......
它使语言规范膨胀了很多。
这完全违背了普通用户的简单性......
那么为什么要声明呢?反正历史已经是一团糟了。似乎大多数语言设计者都没有仔细选择他们的选择。
更糟糕的是,它甚至给了一些类型系统爱好者(他们对 PL 历史不够熟悉)一些误解,即类型系统必须与操作语义上更基本的规则设计有关。
说真的,在许多情况下,根据类型进行推理并没有那么糟糕,但在这种特殊情况下尤其没有建设性。即使是专家也可以搞砸。
例如,有人强调打字良好的性质as the central argument against the traditional treatment of undelimited continuations。尽管结论有些合理,并且关于组合函数的见解还可以(but still far too naive to the essense),但这个论点并不合理,因为它完全忽略了实践中的“侧通道”方法,如 _Noreturn any_of_returnable_types
(在 C11 中)对 Falsum
进行编码. 而且严格来说,状态不可预测的抽象机器并不等同于“崩溃的计算机”。
语句是构建所有 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;}
:)
我更喜欢 statement
在形式逻辑意义上的含义。它可以改变计算中一个或多个变量的状态,从而能够对它们的值做出真或假的陈述。
我想,当引入新的术语或新词、现有词被“重新利用”或用户不了解他们所描述的现有、已建立或“适当”的术语时,计算世界和一般科学总会出现混乱
这是我找到的最简单答案之一的摘要。
最初由 Anders Kaseorg 回答
语句是执行某些操作的完整代码行,而表达式是计算结果为值的代码的任何部分。
表达式可以使用运算符“水平”组合成更大的表达式,而语句只能通过一个接一个地编写或使用块结构来“垂直”组合。
每个表达式都可以用作语句(其作用是评估表达式并忽略结果值),但大多数语句不能用作表达式。
陈述是语法上完整的句子。表达式不是。例如
x = 5
读作“x 得到 5”。这是一个完整的句子。编码
(x + 5)/9.0
读作“x 加 5 全部除以 9.0”。这不是一个完整的句子。该声明
while k < 10:
print k
k += 1
是一个完整的句子。请注意,循环头不是; “while k < 10”是从句。
while
是一些语言的表达式,例如 Scala。您将语法与打字混为一谈。请参阅my answer。
while
在 Scala 中仍然是一个表达式。如果它产生副作用,它也可能是一个声明,这是我严重否决的答案所允许的(表达式也可以是一个声明)。我的答案是唯一正确的。不能理解的读者请见谅。
(x + 5)/9.0
绝对可以作为一个语句独立存在。此外,如果通过语法完整,您的意思是一个有效的程序,C 不允许语句作为单个程序独立存在。
在面向语句的编程语言中,代码块被定义为语句列表。换句话说,语句是一段语法,您可以将其放入代码块中而不会导致语法错误。
维基百科类似地定义了语句这个词
在计算机编程中,语句是命令式编程语言的句法单元,表示要执行的某些操作。用这种语言编写的程序由一系列的一个或多个语句组成
注意后一种说法。 (尽管在这种情况下,“一个程序”在技术上是错误的,因为 C 和 Java 都拒绝一个不包含任何语句的程序。)
维基百科将单词表达式定义为
编程语言中的表达式是可以被评估以确定其值的句法实体
然而,这是错误的,因为在 Kotlin 中,throw new Exception("")
是一个表达式,但在计算时,它只是抛出一个异常,从不返回任何值。
在静态类型的编程语言中,每个表达式都有一个类型。但是,此定义不适用于动态类型的编程语言。
就个人而言,我将表达式定义为一段语法,它可以与运算符或函数调用组合以产生更大的表达式。这其实类似于维基百科对表达的解释:
它是编程语言解释(根据其特定的优先级和关联规则)并计算产生(“返回”,在有状态的环境中)另一个值的一个或多个常量、变量、函数和运算符的组合
但是,问题出在 C 编程语言中,给定一个这样的函数 executeSomething:
void executeSomething(void){
return;
}
executeSomething()
是表达式还是语句?根据我的定义,它是一个语句,因为在微软的 C 引用语法中定义,
您不能以任何方式使用具有 void 类型的表达式的(不存在的)值,也不能将 void 表达式(通过隐式或显式转换)转换为除 void 之外的任何类型
但是同一页清楚地表明这种语法是一个表达式。
语句是一个不返回任何内容的代码块,它只是一个独立的执行单元。例如-
if(a>=0)
printf("Hello Humen,I'm a statement");
另一方面,表达式返回或计算一个新值。例如 -
if(a>=0)
return a+10;//This is an expression because it evalutes an new value;
或者
a=10+y;//This is also an expression because it returns a new value.
一段可以被评估为某个值的语法。换句话说,表达式是表达式元素的集合,如文字、名称、属性访问、运算符或函数调用,它们都返回一个值。与许多其他语言相比,并非所有语言结构都是表达式。还有一些语句不能用作表达式,例如while。赋值也是语句,而不是表达式。
语句是套件的一部分(代码“块”)。语句可以是表达式,也可以是带有关键字的多个结构之一,例如 if、while 或 for。
为了改进和验证我之前的回答,编程语言术语的定义应在适用时从计算机科学类型理论中解释。
表达式具有不同于底部类型的类型,即它具有值。语句具有 Unit 或 Bottom 类型。
由此可见,一个语句只有在产生副作用时才能在程序中产生任何影响,因为它要么不能返回值,要么只返回不可赋值的 Unit 类型的值(在某些语言中,例如C 的 void
) 或(例如在 Scala 中)可以存储以用于语句的延迟评估。
显然 @pragma
或 /*comment*/
没有类型,因此与语句不同。因此,唯一没有副作用的语句类型是非操作。非操作仅可用作未来副作用的占位符。由于声明而导致的任何其他操作都将是副作用。同样,编译器提示(例如 @pragma
)不是语句,因为它没有类型。
@pragma
或 /*comment*/
的不同处理在逻辑上是不一致的。
最准确地说,一个语句必须有一个“副作用”(即be imperative)并且一个表达式必须有一个值类型(即不是底部类型)。
type of a statement 是单位类型,但由于停止定理,单位是虚构的,所以可以说 bottom type。
Void
不完全是底部类型(它不是所有可能类型的子类型)。它存在于 don't have a completely sound type system 的语言中。这听起来像是一种势利的说法,但完整性 such as variance annotations 对于编写可扩展软件至关重要。
让我们看看维基百科对这个问题有什么看法。
https://en.wikipedia.org/wiki/Statement_(computer_science)
在计算机编程中,语句是命令式编程语言中最小的独立元素,它表示要执行的某些操作。许多语言(例如C)对语句和定义进行了区分,语句仅包含可执行代码,定义声明标识符,而表达式仅计算值。
pass
是一个语句。这是一个空操作,它不会评估任何东西。
不定期副业成功案例分享
callfunc(x = 2);
将x
传递给callfunc
,而不是2
。如果x
是浮点数,将调用callfunc(float)
,而不是callfunc(int)
。在 C++ 中,如果您将x=y
传递给func
,并且func
获取一个引用并更改它,它会更改x
,而不是y
。where
子句被认为是表达式而不是语句。 learnyouahaskell.com/syntax-in-functions#wherewhere
实际上是函数声明的一部分,而不是表达式或语句。