繁体   English   中英

全局变量扰乱了我的递归函数

[英]Global variable messes up my recursive function

我刚遇到一个棘手的问题。 以下代码应该将单词拆分为长度为numOfChar块。 该函数调用自身,这使得无法在函数内部生成结果列表( res )。 但是如果我把它作为全局变量保留在外面,那么每次后续调用具有不同输入值的函数都会导致错误的结果,因为res不会被清除。

谁能帮我吗?

这是代码(如果您感兴趣,这是来自PySchools.com的问题7-23 ):

res = []

def splitWord(word, numOfChar):        
    if len(word) > 0:
        res.append(word[:numOfChar])
        splitWord(word[numOfChar:], numOfChar)    
    return res

print splitWord('google', 2)
print splitWord('google', 3)
print splitWord('apple', 1)
print splitWord('apple', 4)

纯递归函数不应该修改全局状态,这算作副作用。

请尝试以下方法,而不是追加和递归;

def splitWord(word, numOfChar): 
    if len(word) > 0:
        return [word[:numOfChar]] + splitWord(word[numOfChar:], numOfChar)
    else:
        return []

在这里,你将这个单词一次一块地切成碎片,在每次通话的同时将其切断,然后在上升时将这些碎片重建成一个列表。

这是一种称为尾递归的常见模式。

PS As @ e-satisf指出,递归不是在Python中执行此操作的有效方法。 另请参阅@ e-satisf的答案,以获得更精细的尾递归示例,以及使用生成器解决问题的更多Pythonic方法。

这里完全不需要递归:

def splitWord(word, numOfChar):
   return [word[i:i+numOfChar] for i in xrange(0, len(word), numOfChar)]

如果你坚持使用递归解决方案,那么避免使用全局变量是一个好主意(它们使得理解正在发生的事情变得非常棘手)。 这是一种方法:

def splitWord(word, numOfChar):
   if len(word) > 0:
      return [word[:numOfChar]] + splitWord(word[numOfChar:], numOfChar)
   else:
      return []

要详细说明@Helgi的答案,这里是一个更高效的递归implémentation。 它更新列表而不是对两个列表求和(这导致每次都创建一个新对象)。

此模式强制您将列表对象作为第三个参数传递。

def split_word(word, num_of_chars, tail):

    if len(word) > 0:
        tail.append(word[:num_of_chars])
        return split_word(word[num_of_chars:], num_of_chars, tail)

    return tail

res = split_word('fdjskqmfjqdsklmfjm', 3, [])

这种形式的另一个优点是它允许尾递归优化。 它在Python中没用,因为它不是执行此类优化的语言,但是如果将此代码转换为Erlang或Lisp,您将免费获得它。

请记住,在Python中,您受到递归堆栈的限制,并且没有办法摆脱它。 这就是递归不是首选方法的原因。

您最有可能使用yielditertools (一个操作生成器的模块)来使用生成器。 这是一个非常好的函数示例,它可以拆分任何可迭代的块:

from itertools import chain, islice

def chunk(seq, chunksize, process=iter):
    it = iter(seq)
    while True:
        yield process(chain([it.next()], islice(it, chunksize - 1)))

如果你开始学习Python,现在有点复杂,所以我不希望你现在完全掌握它,但是你可以看到它并且知道它存在是很好的。 稍后你会再回过头来看看(我们都做过,Python迭代工具最初是压倒性的)。

这种方法的好处是:

  • 它可以嵌套任何可迭代的,不仅仅是字符串,还有列表,字典,元组,流,文件,集,查询集,你可以命名它......
  • 它接受任意长度的迭代,甚至是一个长度未知的迭代(在这里考虑字节流)。
  • 它占用很少的内存,因为生成器最好的事情是它们一个接一个地生成值,并且在计算下一个之前它们不存储先前的结果。
  • 它返回任何性质的块,这意味着你可以拥有一大块x个字母,x个项目列表,甚至生成器吐出x个项目(这是默认值)。
  • 它返回一个生成器,因此可以用于其他生成器的流程。 将数据从一个生成器管道化到另一个生成器,bash样式,是一种很棒的Python功能。

要获得与您的函数相同的结果,您可以:

In [17]: list(chunk('fdjskqmfjqdsklmfjm', 3, ''.join))
Out[17]: ['fdj', 'skq', 'mfj', 'qds', 'klm', 'fjm']

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM