【问题标题】:How to keep count in a recursive function?如何在递归函数中保持计数?
【发布时间】:2010-01-10 10:57:12
【问题描述】:

我写了一个递归函数来查找父字符串中子字符串的实例数。

我保持计数的方式是将count 声明/初始化为函数范围之外的全局变量。问题是,它只会在函数第一次运行时给我正确的结果,因为在那之后count != 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

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

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

【问题讨论】:

  • 请解释一下你理解的“ 所以”是什么意思;无论任务是否为家庭作业,使用字符串模块对于 Python >= 1.6 来说都是无稽之谈。

标签: python recursion


【解决方案1】:

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

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 用于使用本地函数,因为它维护了外部函数的规范。
  • 我更喜欢使用闭包(例如装饰器)以保持目标函数像以前一样简单,并将计数的责任委托给实际的装饰器。
【解决方案2】:

您的递归函数具有 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)

【讨论】:

    【解决方案3】:

    附带说明:所有提出的解决方案(从最初的 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】:

    这与 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))
    

    【讨论】:

      【解决方案5】:

      我正在 OpenCourseware 上做这门课程,非常棒。无论如何,这就是我所做的。我从上面的 adamse 中获得了灵感。

      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
      

      【讨论】:

        【解决方案6】:

        考虑到重叠的出现并维护来自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

        “香蕉”中“ana”的实例数:2

        【讨论】:

          【解决方案7】:
          def countSubStringMatchRecursive(target, key):
              index = string.find(target, key)
              if index == -1:
                  return 0
              else:
                  return 1 + countSubStringMatchRecursive(target[index + len(key):], key)
          

          【讨论】:

            【解决方案8】:

            这个怎么样?

            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
            

            【讨论】:

              【解决方案9】:

              未经测试...

              代码:

              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?您正在使用的书籍或教程的最后更新日期是什么时候?

              从字符串模块开始(它将在您的计算机上以&lt;your Python install directory&gt;/Lib/string.py 的形式出现;我引用的是 2.6 版本):

              """字符串操作的集合(大部分不再使用)。

              警告:您在此处看到的大部分代码现在通常不使用。 从 Python 1.6 开始,其中许多函数都实现为 标准字符串对象上的方法。它们曾经由 一个名为 strop 的内置模块,但 strop 现在本身已过时。

              等 """

              这里是 find 函数的文件代码(去除 cmets):

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

              所以使用string.find(target, key) 而不是target.find(key) 是一种浪费。

              【讨论】:

              • 家庭作业...来自 MIT_OCW... #### index = find(target, key) # 嗯?哎呀!我错过了代码 sn-p 中的字符串导入 *。谢谢
              • 字符串模块:yuk.请参阅我的增强答案。
              • 顺便说一下(1)from anymodule import *不是个好主意;最好明确导入您实际需要的对象。 (2) 每个答案的左上角都有一个叫做“upvote button”的东西;-)
              【解决方案10】:

              我建议使用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

              【讨论】:

                【解决方案11】:

                另一种方法是在 countSubStringMatchRecursive 函数上添加第三个可选参数,称为 count,最初设置为 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 的解决方案。它在与外部的交互和“业务逻辑”之间进行了清晰的分离,使代码更具可重用性!

                【讨论】:

                • 显然这也可以将count 更改为全局变量,但如果对函数有很多函数调用,它可能会更疯狂,count 必须在上面定义函数已定义。
                【解决方案12】:
                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: "+str(count))
                

                您可能会注意到我的代码和那个代码有点不同。为什么?答案是,使用该函数将数字变为 1 所需的步骤数是最后一次调用不包含在我的函数中,因为它是之前调用的重复。

                【讨论】:

                  【解决方案13】:

                  这个答案太方便了,我也必须在这个帖子中分享:

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

                  (对函数赋值的使用)

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2017-06-24
                    • 2021-12-13
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-02-28
                    相关资源
                    最近更新 更多