操作符优先级

3

考虑这个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的赋值从未发生,因此第一行创建的第一个节点并没有“丢失”。 - Andreas Tasoulas
@atas:请查看新的编辑。 - Betamoo
4个回答

2
在Python中,您不能像您所做的那样分组赋值。极端右侧的值被分配给其左侧的所有标识符。
>>> 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() 怎么样? - Betamoo
要使 b.next 可用,它应该已经被实例化。然后再进行从右到左的处理。 - Senthil Kumaran

2
此行为不是操作符优先级所致,而是Java、Python和C#中规定表达式求值顺序的规则。具体而言,在Java、Python和C#中,表达式从左到右进行求值。例如,在C和C++中,你所写的表达式结果将是未定义的,这与Java、Python和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#从左到右评估赋值的两侧。

所以在您的第二种情况中,在最后一行的赋值中“评估”LHS(B.Next)之前,它会评估RHS,并到达A中Next的引用。

无论您在RHS上做什么都将分配给此内容。 因此,在RHS上更改B为时已经太晚了。

如果不这样做,则

B.Next = (B = null)

那将会是一个 null 引用异常,这不是你所期望的结果,对吗?


1

为了完整性,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的效果相同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接