【问题标题】:Operator precedence运算符优先级
【发布时间】:2011-07-07 22:20:26
【问题描述】:

考虑这个 C# 类:

class Node
{
    public Node Next;
}

并考虑这两种情况:

        Node A = new Node();
        Node B = A;
        B=(B.Next = new Node());

        Node A = new Node();
        Node B = A;
        B.Next = (B=new Node());

为什么它们会产生相同的结果!?
(A)->(B)->Null

我认为由于运算符优先级,第二种情况会产生一个指向自身的节点...

JavaPython 也是这样吗? 谢谢

【问题讨论】:

  • (A = new Node()) 应该返回一个新节点。似乎对 A 的分配从未发生过,所以在第一行创建的第一个节点并没有“丢失”。
  • @atas:请看一下新的编辑..

标签: c# java python operator-keyword operator-precedence


【解决方案1】:

这种行为不是运算符优先级的结果,而是 Java、Python 和 C# 中定义表达式求值顺序的规则的结果。具体来说,在 Java、Python 和 C# 中,表达式是从左到右计算的。这是不正确的,例如,在 C 和 C++ 中,您编写的结果将是未定义的。

你可能对这个 C 谜题很熟悉:

int i = 1;
printf("%d, %d\n", i++, ++i); // what is printed?

在 C 中,结果没有定义。可以是1, 3,也可以是2, 2,甚至是别的什么。在 Java C# 和 Python 中,结果始终为 1, 3(当然 Python 没有前置或后缀 ++ 运算符除外)。

运算符优先级是一个单独的问题。它定义了解析树,而不是评估的顺序。

假设您有一种带有二进制中缀运算符op1op2 的新语言。假设你有以下代码:

e1 op1 e2 op2 e3

运算符优先级告诉你这是否意味着

((e1 op1 e2) op2 e3)

或者是否意味着

(e1 op1 (e2 op2 e3))

它确实告诉您评估 e1e2e3 的顺序。

【讨论】:

    【解决方案2】:

    C# 从左到右计算赋值的两边。

    因此,在您的第二种情况下,它是在评估 RHS 之前在最后一行的赋值中“评估”LHS (B.Next),并在 A 中获得对 Next 的引用。

    您在 RHS 上所做的任何事情都会分配给此。所以在 RHS 上更改 B 为时已晚。

    如果它没有这样做,那么

    B.Next = (B = null)
    

    会是一个空引用异常,这不是您所期望的吗?

    【讨论】:

      【解决方案3】:

      在 Python 中,您不能按照您已经完成的方式对作业进行分组。极端 RHS 值分配给其左侧的所有标识符。

      >>> a = (b=42)
        File "<stdin>", line 1
          a = (b=42)
                ^
      SyntaxError: invalid syntax
      >>> a = b = 42
      
      >>> print a,b
      42 42
      >>> 
      

      与您的问题类似

      >>> class Node():
      ...     def __init__(self):
      ...             self.next = self
      ... 
      >>> n = Node()
      >>> n = n.next = Node()
      >>> n
      <__main__.Node instance at 0x7f07c98eb200>
      >>> n.next = n = Node()
      >>> n
      <__main__.Node instance at 0x7f07c98eb290>
      

      【讨论】:

      • 好的,那么 b=b.next=Node() 和 b.next=b=Node() 呢??
      • 为了使 b.next 可用,它应该已经被实例化了。然后又是从右到左的过程。
      【解决方案4】:

      为了完整起见,Java 在这里的工作方式与 C# 相同。

          Node A = new Node();
      

      创建一个新节点(我们将其命名为节点 1),并让 A 指向它。

          Node B = A;
      

      这让 B 指向同一个节点 1。

      A ----> [Node 1]---> null
                 ↑
      B −−−−−−−−−'
      

      这就是现在的情况。所以,现在是复杂的最后一行:

          B.Next = 
      

      这会将节点 1(B 指向的)的下一个指针更改为 ...

             (B=new Node());
      

      ... 一个新节点(我们称之为节点 2),同时放入 B 变量中。

      A ----> [Node 1]---> [Node 2]----> null
                               ↑
      B −−−−−−−−−−−−−−−−−−−−−−−'
      

      这里有趣的是,B.next 在赋值右侧之前被评估,所以它就像你在这里写A.next 一样。

      【讨论】:

        猜你喜欢
        • 2011-06-21
        • 2013-02-24
        • 2012-08-10
        • 2020-03-05
        • 2012-12-13
        • 2017-06-02
        • 2015-03-20
        相关资源
        最近更新 更多