如何在递归函数中保持计数? [蟒蛇]

时间:2010-01-10 10:57:12

标签: python recursion

我写了一个递归函数来找到no。父字符串中子字符串的实例。 我保持计数的方式是将计数声明/初始化为函数范围之外的全局变量。问题是,它只会在第一次运行函数时给我正确的结果,因为在那之后计数!= 0开始。如果我在函数内部使用它,那么每次递归调用它时,它都将设置为0.

count=0
def countSubStringMatchRecursive(target,key):
    index=find(target,key)
    global count
    targetstring=target
    if index>=0:
        count=count+1
        target=target[index+len(key):]
        countSubStringMatchRecursive(target,key)
    else :
        pass
    return "No. of instances of", key, 'in', targetstring, 'is', count

注意:我正在寻找具体的recursive函数的解决方案,我有一个可以正常工作的迭代函数。

编辑:谢谢大家,这是作业的一部分,所以我只使用字符串模块

13 个答案:

答案 0 :(得分:13)

修改代码的一种方法是使用本地函数,如下所示:

def countSubStringMatchRecursive(target,key):
    def countit(target,key,count):
        index=find(target,key)
        if index>=0:
            target=target[index+len(key):]
            count += countit(target,key,count) + 1
        return count
    return "No. of instances of", key, 'in', target, 'is', countit(target,key,0)

答案 1 :(得分:9)

您的递归函数具有O(n ^ 2)性能,因为它每次找到匹配项时都会复制字符串的剩余内容。这比迭代解决方案O(n)慢,并且不必要地如此。

您可以轻松地将其重写为更快,同时通过将搜索的起始索引作为可选参数传递给函数来简化代码并扩展其功能:

def countSubStringMatchRecursive(target, key, start_index = 0):
    index = target.find(key, start_index)
    if index >= 0:
        return countSubStringMatchRecursive(target, key, index + len(key)) + 1
    return 0

target_string = 'an apple and a banana'
key = 'an'
count = countSubStringMatchRecursive(target_string,  key)
print "Number of instances of %r in %r is %d" % (key, target_string, count)

输出:

Number of instances of 'an' in 'an apple and a banana' is 4

更新:如果您真的想使用字符串模块的查找功能,只需更改一行即可完成此操作:

index = find(target, key, start_index)

答案 2 :(得分:6)

这与Greg Hewgill的答案类似。但是,我们每次调用函数时都会传递当前计数,然后在没有更多匹配项时返回计数。虽然我怀疑它在Python中没有区别,但在实现尾调用递归的语言中,这允许在调用堆栈上对每个连续的do_count调用进行优化。这意味着每次调用do_count都不会导致调用堆栈增长。

def count_sub_strings(target, key):
    def do_count(target, key, count):
        index = target.find(key)
        if index >= 0:
            target = target[index + len(key):]
            return do_count(target, key, count + 1)
        else:
            return count
    return "No. of instances of %s in %s is %s" % (key, target, do_count(target, key, 0))

答案 3 :(得分:6)

只是旁注:提出的所有解决方案(从原始Q到所有As)都解决了一个与具体说明的问题不同的问题(我想这是特定问题陈述中的一个错误,但是,如果有的话,值得修复;-)。考虑:

>>> 'banana'.count('ana')
1
>>> sum('banana'[x:x+3]=='ana' for x in range(len('banana')))
2

第一个表达式是计算'banana'中'ana'出现的非重叠;第二个是计算所有次出现 - 总共有两次出现,在'banana'中的索引1和3处,它们重叠。所以给出问题陈述,我引用:

  找不到。的例子   父字符串中的子字符串。

没有提及“非重叠”,似乎应计算的重叠事件。当然,这很容易修复,一旦注意到 - 你只需要每次前进1,而不是前进len(key),这会导致你跳过重叠的事件。

所以,例如:

import string

def countit(target, key, startfrom=0):
    where = string.find(target, key, startfrom)
    if where < 0: return 0
    return 1 + countit(target, key, where+1)

print countit('banana', 'ana')

打印2,计算两个(重叠)出现次数。

答案 4 :(得分:3)

我正在OpenCourseware上学这门课程,这很棒。无论如何,这就是我所做的。我从上面的亚当人那里获取灵感。

def countSubStringMatchRecursive(target, key, counter = 0):
    if find(target,key) == 0:
        countSubStringMatchRecursive(target[1:], key, counter + 1)
    elif find(target,key) > 0:
        countSubStringMatchRecursive(target[1:], key, counter)
    elif find(target,key) == -1:
        print counter

答案 5 :(得分:3)

考虑到重叠事件并保持MIT的原始定义,这是我能得到的更简单,更紧凑的代码。

代码:

from string import *
def countSubStringMatchRecursive(target, key):
    index = find(target, key)
    if index > -1:
        return countSubStringMatchRecursive(target[index + 1:], key) + 1
    return 0


def test(target, key):
    instances = countSubStringMatchRecursive(target, key)
    if instances == 0:
        print "No instance of %r in %r" % (key, target)
    else:
        print "Number of instances of %r in %r: %d" % (key, target, instances)

test("atgacatgcacaagtatgcat","ggcc")
test("atgacatgcacaagtatgcat","atgc")
test("banana", "ana")

输出:

'atgacatgcacaagtatgcat'中没有'ggcc'的实例

'atgacatgcacaagtatgcat'中'atgc'的实例数:2

'banana'中'ana'的实例数:2

答案 6 :(得分:2)

def countSubStringMatchRecursive(target,key):
index = string.find(target, key)
if index == -1:
    return 0
else:
    return 1 + countSubStringMatchRecursive(target[index+len(key):],key)

答案 7 :(得分:1)

这个怎么样?

def count_it(target, key):
    index = target.find(key)
    if index >= 0:
        return 1 + count_it(target[index+len(key):], key)
    else:
        return 0


print count_it("aaa bbb aaa ccc aaa", "aaa")

输出:

3

答案 8 :(得分:1)

未经测试...

代码:

def countSubStringMatchRecursive(target, key, count=0):
    #### index = find(target, key) # HUH?
    index = target.find(key)
    if index >= 0:
        count += 1
        target = target[index+len(key):]
        count = countSubStringMatchRecursive(target, key, count)
    return count

for test in ['', 'bar', 'foo', 'foofoo', 'foo foo foo fo']:
   print countSubStringMatchRecursive(test, 'foo'), test.count(key), repr(test)

输出:

0 0 ''
0 0 'bar'
1 1 'foo'
2 2 'foofoo'
3 3 'foo foo foo fo'

我认为这只是娱乐或家庭作业......递归函数必须比相应的Python迭代解决方案慢,这自然比使用target.count(key)慢...所以我没有打扰修复你的版本所有的问题......但是请阅读PEP-008: - )

对字符串模块的评论

您评论说您遗漏了from string import find。您使用的是哪个版本的Python?您正在使用的书籍或教程的最后更新日期是什么?

从字符串模块的开头(它将作为<your Python install directory>/Lib/string.py在您的计算机上;我引用的是2.6版本):

“”“字符串操作的集合(大多数不再使用)。

警告:您现在看到的大多数代码现在都不常用。 从Python 1.6开始,许多这些函数都实现为 标准字符串对象上的方法。它们曾经被实施过 一个名为strop的内置模块,但strop现在已经过时了。

等 “”“

这是find函数的文件代码(删除了注释):

def find(s, *args):
    return s.find(*args)

所以使用string.find(target, key)代替target.find(key)是浪费。

答案 9 :(得分:1)

我建议使用 singleton class 模式来跟踪递归函数中的计数器值。

简单来说,单例类模式在运行时只会创建一个实例,并且会限制为一个类创建多个实例。(实时单例类的例子:日志服务、数据库连接服务、运行中的文件系统系统)

这里包括使用单例类方法的递归函数的计数器代码:


class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]
        
class CounterSingleton(object):
    __metaclass__ = Singleton


    def __init__(self):
        self.counter = 0
        self.data = []

    def update(self):
        self.counter += 1
    
    def update_log(self, val):
        self.data.append((val, self.counter))

    def get(self):
        return self.counter


count_single = CounterSingleton()



def countSubStringMatchRecursive(target,key):
    index=find(target,key)
    targetstring=target
    if index>=0:
        count_single.update()
        target=target[index+len(key):]
        countSubStringMatchRecursive(target,key)
    else :
        pass
    return "No. of instances of", key, 'in', targetstring, 'is', count_single.get()

参考:https://en.wikipedia.org/wiki/Singleton_pattern

答案 10 :(得分:0)

另一种方法是在名为count的countSubStringMatchRecursive函数上设置第三个可选参数,该参数最初设置为0。这样你就可以跟踪计数。这会将count变量暴露给外部世界,这可能是不可取的,但由于它并不比你的全局变量差,我认为这不会是你的问题。

您还必须更改代码以使最后一次递归调用成为将return语句提供给外部世界的调用。请参阅此示例(未经测试):

def countSubStringMatchRecursive(target, key, count = 0):
    index = find(target, key)
    targetstring = target
    if index >= 0:
        count += 1
        target = target[index+len(key):]
        countSubStringMatchRecursive(target, key, count)
    else:
        return "No. of instances of", key, 'in', targetstring, 'is', count

编辑:我意识到你需要第四个参数才能保持原始字符串沿着递归传播。这可能不是最佳解决方案,我建议使用Greg Hewgill的解决方案。它与外部交互和“业务逻辑”之间有一个清晰的分离,使代码更可重用!

答案 11 :(得分:0)

steps = 0
def addPersistence(number, steps):
    steps += 1
    if len(str(number))==1:
        print(str(number) + "\nDone---------------------------------------------------")
        print("TOTAL STEPS " + str(steps-1))

    digits = [int(i) for i in str(number)]

    result = 0
    for j in digits:
        result += j


    if len(str(number)) != 1:
        print(number)
        addPersistence(result, steps)

这是从我的一个项目中复制的示例。此函数用于确定数字的加法持久性(即将数字的数字相加并重复直到数字变为1),但是它肯定可以重用,您可以像下面这样使用函数(这是Python 3代码) ,如果您想将其更改为Python 2,它将仍然有效):

count=0
def countSubStringMatchRecursive(target,key,count):
    index=find(target,key)
    targetstring=target
    if index>=0:
        count+=1
        target=target[index+len(key):]
        countSubStringMatchRecursive(target,key,count)
    else :
        pass
    print("STEPS: "+count)

您可能会注意到我的代码,并且该代码有些不同。为什么?答案是,使用该函数将数字升为1所需的步骤数是我的函数未包含最后一个调用,因为这是在此之前的重复调用。

答案 12 :(得分:0)

这个答案太方便了,我也必须在这篇文章中分享:

https://stackoverflow.com/a/41203348/6146879

(使用赋值给函数)