我正在学习Python的递归。我定义了一个链表,其中每个节点都有item和next。我想写一个递归,将奇数和偶数放在一个单独的集合中。
class LinkNode(object):
"""A node in a linked list."""
def __init__(self, item, next=None):
"""(LinkNode, object, LinkNode) -> NoneType
Initialize this node to store item and next.
"""
self.item = item
self.next = next
def odd_or_even(self):
"""(LinkNode) -> ([object], [object])
Return a pair of lists: (odd number, even number.
"""
if self is None:
return ([], [])
else:
if (self.item % 2 == 1):
odd_num = odd_num.append(self.item)
else:
even_num = even_num.append(self.item)
if self.next is not None:
self.next.odd_or_even()
return (odd_num, even_num)
当我运行它时,我收到以下错误:
文件“C:\ Program Files \ Wing IDE 101 4.1 \ src \ debug \ tserver_sandbox.py”,第19行,在odd_or_even builtins.UnboundLocalError:赋值前引用的局部变量'odd_num'
我应该在哪里初始化odd_num,even_num所以它不会被覆盖?
感谢。
答案 0 :(得分:0)
if (self.item % 2 == 1):
odd_num = odd_num.append(self.item)
else:
even_num = even_num.append(self.item)
...
return (odd_num, even_num)
上面的代码部分设置odd_num
或even_num
的值,但不能同时设置两者。然后它会尝试返回odd_num
和even_num
,这会产生一个或另一个错误。如果在None
语句之前将{2}初始化为if
,则可以避免发生的错误。但是,要使语义工作,您可能需要向函数添加另一个参数,即上一级的结果;在递归调用中,将刚刚计算的odd_num
或even_num
传递给下一级别;然后返回下一级别的结果。但是,避免递归可能会更好,而是有一个运行两次的循环。
答案 1 :(得分:0)
我认为您可以使用几种不同的方法。
一种是使用全局变量。我不是真的推荐这个,但它很容易理解,所以我先介绍它:
even_num = [] # these should be at module level, but I'm not showing the class
odd_num = [] # so you'll have to imagine the indentation being correct
def odd_or_even(self):
"""(LinkNode) -> ([object], [object])
Return a pair of lists: (odd number, even number.
"""
if self.item % 2 == 1:
odd_num.append(self.item)
else:
even_num.append(self.item)
if self.next is not None:
self.next.odd_or_even()
对代码的更改很小,主要是删除return
语句。我确实对self
None
进行了初步检查,因为在方法中这是不可能的(除非调用者非常努力地实现它)。值得注意的是,我们不会尝试直接分配给odd_num
或even_num
,因为这会创建一个局部变量,而不是访问已经存在的全局变量。此解决方案的缺点是您只能调用odd_or_even
一次并使其正常工作。如果你想再次调用它(可能在另一个列表中),你需要重新初始化全局变量。
这是一种更好的方法,它将偶数和奇数列表创建为局部变量,然后在结尾处返回它们。这可能是您在代码中的目标,但是您错过了将递归调用的结果添加到列表中的步骤。
def odd_or_even(self):
even_num = []
odd_num = []
if self.item % 2 == 1:
odd_num.append(self.item)
else:
even_num.append(self.item)
if self.next is not None:
next_even, next_odd = self.next.odd_or_even()
even_num.extend(next_even)
odd_num.extend(next_odd)
return even_num, odd_num
此代码的问题在于创建和扩展列表是浪费的。在递归的每个级别上,将创建两个新列表,即使在该级别仅处理一个值。对于整个递归过程,更好的方法总共只能使用两个列表:
def odd_or_even(self, lists=None):
if lists is not None:
even_num, odd_num = lists
else:
even_num = []
odd_num = []
if self.item % 2 == 1:
odd_num.append(self.item)
else:
even_num.append(self.item)
if self.next is not None:
return self.next.odd_or_even((even_num, odd_num))
else:
return even_num, odd_num
这比以前的版本更有效,因为它在递归的所有级别上使用相同的列表。在其他一些编程语言中,这种递归方式(其中所有工作都在运行递归调用之前完成)比其他类型的递归更有效。这是因为编译器可以优化函数的return
步骤(并且只重用堆栈帧)。但是,Python不进行这样的“尾部调用优化”(因为它会弄乱堆栈跟踪),因此它们的好处并不像它们可能的那么大。
要考虑的另一件事:您可以使用自己的链表类,而不是Python列表来保存偶数和奇数值。我在上面展示的第二个和第三个解决方案都可以工作,但如果你愿意以相反的顺序返回偶数和奇数值,那么第三个解决方案最有效。