C&Python:表达式的求值顺序(evaluation order)

相关阅读

Pythonhttps://blog.csdn.net/weixin_45791458/category_12403403.html?spm=1001.2014.3001.5482


C中表达式的求值

C语言针对表达式的计算,设置了操作符的优先级和结合性这两个特性,优先级用于解析不同优先级的符号,结合性用于解析相同优先级的符号。但是这两个特性并不能完全确定表达式的计算顺序,这就给编译器留下了一定的优化的空间,下面举例说明这一点。假设有如下所示的简单表达式。

例1
1 + 2 + 3

C语言编译器在语法分析时会构建一个语法分析树,类似图1所示的二叉树结构。

图1 语法分析树

在图1中,1+2整体作为一个子表达式成为了+操作符的左操作数,这是操作符的结合性导致的结果。这个语法分析树保证了,3和1+2会在根节点"+"前求值,1和2会在"+"的左子节点"+"前求值。但是,这并没有保证3和1+2的求值顺序,也没有保证1和2的求值顺序,具体地来说编译器可能选择先对3求值,随后对1+2求值,在对1+2求值时,先对2求值,再对1求值;也可能选择先对1+2求值,在对1+2求值时,先对1求值,再对2求值,最后对3求值....还有其他情况。

这看似对最终表达式结果并没有什么影响(不管哪种求值顺序,结果都是6),但如果将简单的操作数换成函数调用,则会出现不同的情况,如下例所示。

例2
func1()+func2()+func3()

int func1()
{
    printf("This is func1.\n");
    return 1; 
}

int func2()
{
    printf("This is func2.\n");
    return 2; 
}

int func3()
{
    printf("This is func3.\n");
    return 3; 
}

在这个例子中,三个函数的执行顺序是不确定的,可能是func1、func2、func3,可能是func2、func1、func3,可能是func3、func1、func2,可能是func3、func2、func1。这就导致了printf语句的执行也是不确定的。

下面再看一个更复杂的例子。

例3
1 + 2 * 3

在这个例子中,由于"*"的优先级大于"+", 2*3整体作为子表达式会成为"+"的右操作数。解析得到的语法分析树如下图2所示。

图2 语法分析树

在这个例子中,2与3的乘法毫无疑问是会在与1的加法前进行的,但是对1、2、3的求值顺序是不确定的,可能是先对1求值,随后对2*3求值,在对2*3求值时,先对2求值,再对3求值;可能是先对2*3求值,在对2*3求值时,先对3求值,再对2求值,最后对1求值...还有其他情况。

在这个简单的例子里,不同的求值顺序对结果没有影响,但如果将1、2、3换成func1、func2、func3,分析是类似的,即三个函数的执行顺序是不确定的(至少是不完全确定的)。

Python中表达式的求值

Python中规定了表达式的求值顺序是从左到右的。就拿上面的例1举例,1+2子表达式一定在3之前求值,而1一定在2之前求值。用数据结构的语言来说,Python保证了在一个语法分析树中,表达式的求值是后序遍历的,即先求值左子节点,后求值右子节点,最后根据操作符求值整个表达式。下面来看一个例子。

例4
func1 + func2 * (func3 - func4)

根据运算符的优先级,这个表达式被解析为图3所示的语法分析树。

图3 语法分析树

根据后序遍历的定义,这四个函数的执行顺序为:func1、func2、func3、func4。详细说就是,"+"的左操作数func1一定会在右操作数func2 * (func3 - func4)前求值;"*"的左操作数func2一定会在右操作数(func3 - func4)前求值;"-"的左操作数func3一定会在右操作数func4前求值。

不止是针对表达式的求值,在Python中表达式列表的求值顺序也是确定的。表达式列表的定义如下所示,即为多个由","分隔的表达式,在函数调用、多变量赋值、函数返回中都有运用。

expression_list ::=  expression ("," expression)* [","]

在如下例的函数调用中,求值的顺序是func1、func2、func3、func4、func5,其中func1也可以被求值,前提函数func1的返回值是一个函数名。

例5
func1(func2, func3, func4, func5)

在如下例的多变量赋值中,求值的顺序是func1、func2、func3。

a, b, c = func1(), func2(), func3()

在如下例的函数返回中,求值的顺序是func1、func2、func3.

return func1(), func2(), func3()

实际上,后两种情况下,表达式列表在被求值后会变成一个包含各表达式求值结果元组,在多变量赋值操作中,元组内的各个元素被赋值给对应的目标变量;在函数返回中,return语句返回一个元组。

相关推荐
搬砖的果果4 分钟前
HTTP代理是什么,主要用来干嘛?
网络·python·网络协议·tcp/ip·http
老汉忒cpp5 分钟前
Qt-窗口相关
开发语言·qt
白初&5 分钟前
文件上传代码分析
java·c++·python·php·代码审计
香菜大丸7 分钟前
详解 指针函数,函数指针,函数指针函数
开发语言·c++·算法
过期的H2O212 分钟前
【H2O2|全栈】JS进阶知识(十)ES6(6)
开发语言·前端·javascript·ecmascript·es6
念怀故安17 分钟前
第十章 JavaScript的应用
开发语言·javascript·ecmascript
ChinaDragonDreamer17 分钟前
HarmonyOS:应用沙箱
开发语言·harmonyos·鸿蒙
White graces20 分钟前
Spring MVC练习(前后端分离开发实例)
java·开发语言·前端·后端·spring·java-ee·mvc
菜鸟小贤贤21 分钟前
pyhton+yaml+pytest+allure框架封装-全局变量渲染
python·macos·pytest·接口自动化·jinja2
赛丽曼35 分钟前
Python中的简单爬虫
爬虫·python