• 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. 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),但如果将简单的操作数换成函数调用,则会出现不同的情况,如下例所示。

    1. 2
    2. func1()+func2()+func3()
    3. int func1()
    4. {
    5. printf("This is func1.\n");
    6. return 1;
    7. }
    8. int func2()
    9. {
    10. printf("This is func2.\n");
    11. return 2;
    12. }
    13. int func3()
    14. {
    15. printf("This is func3.\n");
    16. return 3;
    17. }

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

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

    1. 3
    2. 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保证了在一个语法分析树中,表达式的求值是后序遍历的,即先求值左子节点,后求值右子节点,最后根据操作符求值整个表达式。下面来看一个例子。

    1. 4
    2. 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的返回值是一个函数名。

    1. 5
    2. func1(func2, func3, func4, func5)

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

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

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

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

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

  • 相关阅读:
    使用Docker构建轻量级Linux容器
    javaSE- 方法的使用
    论文阅读笔记(三)——YOLACT Real-time Instance Segmentation
    【软考】4.2 关系代数
    Vue Clipboard 异步复制粘贴
    理解 Redis 新特性:Stream
    开发一个自己的前端脚手架
    .NET EF配置数据库链接
    高德地图,绘制矢量图形并获取经纬度
    TI Sitara系列 AM64x开发板——TSN通信测试手册(上)
  • 原文地址:https://blog.csdn.net/weixin_45791458/article/details/136105999