Python 函数的副作用

Side Effects of Function in Python

提问人:FrankC 提问时间:7/5/2023 最后编辑:FrankC 更新时间:7/5/2023 访问量:73

问:

我是一个相当新手的程序员,目前正在尝试解决 Leetcode 上的“回文链表”程序。我的想法是编写一个名为“reverse”的嵌套函数,它反转链表,然后简单地执行比较。代码附在下面。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        def reverse(arg):
            #function to return a reverse of the linked list
            curr, prev = arg, None
            while curr:
                nxt = curr.next
                curr.next = prev
                prev = curr
                curr = nxt
            return prev
        original = head
        reversed_ = reverse(original)
        return head == reversed_

经过一些调试,我确定我的函数似乎工作正常。但是,当我调用反向时:reverse()

reversed_ = reverse(original)

它有一个意想不到的副作用,即更改 AND 的值。当我在以下输入上运行此代码时:originalhead

head = [1,2,2,1]

然后当我到达最后一行时,具有正确的值,但 head 已被函数调用更改为只是reversed_

head = [1]

我知道这就是所谓的副作用,我现在第一次感受到了函数式编程的吸引力。我能做些什么来纠正这种情况?我尝试引入变量来处理这个问题,但无济于事。original

副作用

评论

0赞 slothrop 7/5/2023
original = head创建为新的变量名称,但该名称引用的对象与 : 不同,它不会创建第二个链表。要按照您想要的方式继续,您需要编写一个函数来复制列表。originalhead
0赞 slothrop 7/5/2023
作为 Python 中名称和对象如何相互关联的一般参考,请参阅 nedbatchelder.com/text/names.html
1赞 slothrop 7/5/2023
@MarkRansom这是一个链表,而不是原生的 Python 列表。 是一个 ListNode,所以不会起作用。(无论如何,仅仅复制一个节点是不够的。headhead[:]
1赞 slothrop 7/5/2023
没错,你可以让你的返回成为列表的反转副本(而不是原地反转)。但最终,如果你想保持原始输入列表不变,就必须在某个地方复制一些节点。reverse
1赞 Grismar 7/5/2023
@MarkRansom Python 调用其数组 - 调用它们会像调用其 .这种混淆是由于人们将“列表”理解为“链表”。arraylistlistarray

答:

0赞 Grismar 7/5/2023 #1

查看代码的这一部分:

        original = head
        reversed_ = reverse(original)
        return head == reversed_

第一行将名为“head”的值命名为“original”,但“head”和“original”都是同一值的名称(可能是 )。ListNode

第二行调用 ,但这与调用 相同。该函数继续更改链表中以 开头的所有对象。结果是一个反转的链表,但由原始链表中的相同元素构成 - 并且由于链表由其部分定义,因此原始链表不再存在。reverseoriginalreverse(head)reverse()ListNodehead

因此,将是 ,因为它将生成的反转链表与以反转链表的最后一个元素为标题的链表进行比较。(这是假设已经正确定义了它)head == reversed_falseListNode__eq__()

为了避免这种情况,您需要首先创建链表的完整副本,然后将其反转,然后比较两个生成的链表(以遵循您提出的逻辑)。或者,您可以通过创建 as you go 的副本来构造反向链表。很难说最好的选择是什么,因为它取决于 .ListNodeListNode

还要考虑可能有更有效的解决方案,可以在不构造副本的情况下对原始链表执行比较,尤其是在您知道链表的长度的情况下。