【问题标题】:What changes should I make to my existent code in order to make it a recursive function?我应该对现有代码进行哪些更改以使其成为递归函数?
【发布时间】:2021-11-20 02:53:47
【问题描述】:

我正在尝试编写一个代码,它将首先返回所有偶数,然后将奇数附加到列表的末尾(顺序无关紧要),我在一些人的帮助下成功地做到了这一点Stack Overflow 用户.. 但是,我想知道有没有一种方法可以递归地执行此操作??

这是原始代码

def evenorodd(s):
    evens = []
    odds = []
    for num in s:
        if num%2 == 0:
            evens.append(num)
        else:
            odds.append(num)
    return evens + odds
print(evenorodd([1,2,3,4,5,6,7,8]))

这个函数工作得很好,但是为了拓宽我对递归函数的理解,有人可以帮忙把这段代码改成递归的,这样我就可以知道我应该怎么做才能让它递归?

【问题讨论】:

  • evenorodd 要递归,它必须调用自己。不过,这个问题似乎真的适合迭代解决方案。更简洁,使用列表推导:[i for i in s if s % 2 == 0] + [i for i in s if s % 2 != 0]
  • 或者如果我们定义odddef odd(n): return n % 2 == 1,我们可以使用sorted来得到你正在寻找的答案:sorted([1,2,3,4,5,6,7,8], key=odd)
  • @Chris sorted(s, key=1 .__and__)
  • 这真的很整洁,也很华丽。

标签: python list


【解决方案1】:

考虑以下问题。我们需要递归的退出条件:当列表为空时。我们有一个累加器,我们从一个迭代传递到下一个迭代,它是两个列表的元组:一个用于偶数,一个用于奇数。

如果列表为空,则每次调用函数时,我们都会连接这两个列表并返回结果列表。

如果列表是_not_empty,我们将列表的部分命名为firstrest 以便于参考。如果first 是偶数,我们将它连接到累加器中的第一个列表,然后调用odd_or_even 与列表的其余部分和更新的累加器。如果它很奇怪,我们也会这样做,但我们会相应地修改累加器。

>>> def odd_or_even(lst, acc=([], [])):
...     if lst == []:
...         return acc[0] + acc[1]
...     else:
...         first = lst[0]
...         rest = lst[1:]
...         if first % 2 == 0:
...             return odd_or_even(rest, (acc[0] + [first], acc[1]))
...         else:
...             return odd_or_even(rest, (acc[0], acc[1] + [first]))
... 
>>> odd_or_even([1, 2, 3, 4, 5])
[2, 4, 1, 3, 5]
>>> 

如果您学习 OCaml、SML 或 Haskell 等函数式编程语言,您会看到很多这样的逻辑。它在 Python 或 Ruby 中不太常见。

【讨论】:

    【解决方案2】:

    在前面插入偶数,在最后插入赔率:

    def evenorodd(s):
        if not s:
            return []
        tmp = evenorodd(s[1:])
        tmp.insert(s[0] % 2 * len(tmp), s[0])
        return tmp
    

    更高效的版本:

    def evenorodd(s):
        s = iter(s)
        for x in s:
            tmp = evenorodd(s)
            tmp.insert(x % 2 * len(tmp), x)
            return tmp
        return []
    

    【讨论】:

      【解决方案3】:

      您的递归版本可以通过传递两个列表来构建结果的左(偶数)和右(奇数)侧,最终将它们连接起来以获得最终结果:

      def evenorodd(s,e=[],o=[]):
          return evenorodd(s[1:], e+s[:1-s[0]%2], o+s[:s[0]%2]) if s else e+o
      
      s = [1,2,3,4,5,6,7,8]
      print(evenorodd(s))
      [2, 4, 6, 8, 1, 3, 5, 7]
      

      【讨论】:

        【解决方案4】:

        所以不推荐这个,但它很有趣。它使用累加器evens 来收集所有偶数值,同时发出所有赔率。

        def evenorodd(s):
            def inner(s, odds):
                if not s:
                    return odds
                
                num = s[0]
                if num % 2 == 0:
                    return [num] + inner(s[1:], odds)
                return [] + inner(s[1:], odds + [num])
        
            return inner(s, [])
        
        print(evenorodd([1,2,3,4,5,6,7,8]))
        

        这很奇怪,但每个值只检查一次。

        编辑:作为奖励,在 Lisp 中也是如此。

        (define inner (s evens)
            (if (null? s)
                evens
                (if (eq (modulo (car s) 2) 1)
                    (cons (car s) (inner (cdr s) evens))
                    (inner (cdr s) (append evens (list (car s)))))))
        
        (define evenorodd(s)
            (inner s '()))
        
        (print (evenorodd '(1 2 3 4 5 6 7 8)))
        

        我刚在我的 iPad 上安装了一个 Lisp 编辑器并想用它来玩,所以你去吧。

        【讨论】:

        • 是否可以只有一个输入,比如'def evenorodd(s, evens):',只能像def evenorodd(s) or evenorodd(even)??跨度>
        • @KellyBundy 糟糕,已修复。
        • @JoshuaBradley 这使用内部函数来完成繁重的工作。外部函数有你想要的签名。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-01
        • 1970-01-01
        • 2015-08-10
        相关资源
        最近更新 更多