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

相关阅读

Pythonicon-default.png?t=N7T8https://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语句返回一个元组。

相关推荐

  1. 前缀表达

    2024-02-15 16:02:02       6 阅读
  2. Excel计算表达

    2024-02-15 16:02:02       38 阅读
  3. 1079:计算分数加减表达

    2024-02-15 16:02:02       13 阅读
  4. C语言:表达式

    2024-02-15 16:02:02       33 阅读
  5. 基于栈后缀算数表达式(头歌)

    2024-02-15 16:02:02       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-15 16:02:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-15 16:02:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-15 16:02:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-15 16:02:02       18 阅读

热门阅读

  1. 探索XGBoost:自动化机器学习(AutoML)

    2024-02-15 16:02:02       33 阅读
  2. USACO 2024 Jan B题解

    2024-02-15 16:02:02       35 阅读
  3. Redis的哨兵系统

    2024-02-15 16:02:02       20 阅读
  4. ARIMA时间序列

    2024-02-15 16:02:02       30 阅读
  5. conda与pip的常用命令

    2024-02-15 16:02:02       35 阅读
  6. 面试官:介绍一下MVC框架

    2024-02-15 16:02:02       29 阅读
  7. 发掘GPT-4商业创新的潜力

    2024-02-15 16:02:02       24 阅读
  8. 数据分析 — Numpy 数组处理

    2024-02-15 16:02:02       25 阅读