双递归教育的例子

时间:2015-06-19 21:02:41

标签: python recursion

这个功能对我来说没有意义。我已经在所有地方添加了印刷语句,以弄清楚发生了什么,我仍然没有得到它。如果有人能向我解释这一点,我将不胜感激。

def f(s):
    if len(s) <= 1:
        return s
    return f(f(s[1:])) + s[0]

print(f("mat"))

这就是我看到的情况。所以我们从长度为3的字符串开始并绕过if语句。我们首先在内部f(s [1:])上工作。所以现在我们有一个长度为2的字符串(&#34; at&#34;)再次绕过if语句并输入f(s [1]),它给出了长度为1的字符串(&#34; t&#34) ;)最后输入if语句并返回&#34; t&#34;。这就是我的道路变冷的地方。

从我的print语句中,我看到一个长度为2的新字符串被创建,随后的#34; a&#34;得到回报。最终产品最终成为&#34; atm&#34;。我得到了&#34; m&#34;由于&#34; + s [0]&#34;被标记在最后部分但为什么它&#34; atm&#34;而不是&#34; tam&#34;?

我老老实实地花了几个小时在这上面,不能下雨。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:5)

通过使用他们正在做的事情填写函数调用,将整个事情扩展为很长的步骤。处理最深/最嵌入的第一个括号。在添加之前处理函数调用。

为了清楚起见,我将忽略所有字符串引号。

f(mat)           -> mat is 3 chars:
                    call f on at for f(at), and call f on that.
                    add m.

f(f(at))+m       -> inner f(at), at is 2 chars:
                    call f on t for f(t), and call f on that.
                    add a.

f(f(f(t))+a)+m   -> innermost f(t) returns t.

f(f(t)+a)+m      -> inner f(t) returns t as well.

f(ta)+m          -> [here, the first f(f(at)) has been reduced to f(ta)]
                    ta is 2 chars:
                    call f on a for f(a), and call f on that.
                    add t.

f(f(a))+t+m      -> inner f(a) returns a.

f(a)+t+m         -> f(a) returns a as well.

a + t + m        -> 

atm              -> everything reduces to atm.

答案 1 :(得分:1)

简短版本:at交换两次,因此内部f("at")调用返回"ta",然后是外{ {1}}调用已通过f()并返回"ta"

更长的版本:我不会明确地为你拼出它,因为你不会那么学习,但考虑这个函数,完全等同于

"at"

当您致电def f2(s): if len(s) <= 1: return s x = f2(s[1:]) return f2(x) + s[0] print(f2("mat")) 时,f2("mat")s[1:]。现在,"at"会返回什么?将该值(f2("at")的值)插入x表达式,看看你得到了什么。

尝试浏览f2(x) + s[0]f2("at")的值,看看您是否能够发现正在发生的事情。如果您在另外15-20分钟的工作后仍然需要帮助,请对此答案发表评论,然后再对其进行扩展。

答案 2 :(得分:1)

这实际上是一个令人惊讶的有趣功能,我可以看出为什么它让你感到困惑。我假设你试图将这个功能理解为一个整体,而这实际上并不适用于此。

现在,这个函数有两个部分 - 处理它是空的情况或字符串中只有一个字母,并处理字符串中至少有两个字母的情况。但是,这是欺骗性的,因为它有效地应用了不同的操作,具体取决于字符串中有多少个字母!

因此,让我们以非递归的方式考虑函数:如果字符串太短,只需返回字符串。否则,将某些函数两次应用于除字符串的第一个字符之外的所有字符,然后将第一个字符添加到结果的末尾。不要认为它是同一个功能,只要把它想象成一些未知的功能。

在代码中:

def f(s):
    if len(s) <= 1:
        return s
    return other_f(other_f(s[1:])) + s[0]

打下兔子洞:

那么我们如何定义这个other_f?让我们看看它对于某些字符串长度需要具有什么样的行为。如果len(s)为2,那么我们知道s[1:]是一个字符,因此other_f只会返回s[1:]。在代码中:

def f2(s): # For when len(s)==2
    #if statement is not used
    #return other_f(other_f(s[1:])) + s[0] becomes
    #return other_f(other_f(s[1])) + s[0] becomes
    #return other_f(s[1]) + s[0] becomes
    return s[1] + s[0]

它只是交换两个字母。让我们使用字符串'abc'来查看下一个字符串的内容:

def f3(s): # For when len(s)==3
    #if statement is not used
    #return other_f(other_f(s[1:])) + s[0] becomes
    #return f2(f2('bc')) + 'a' becomes
    #return f2('cb') + 'a' becomes
    #return 'bc' + 'a'
    return s[1:] + s[0]

因为应用于'bc'的函数交换它们并且被应用两次,所以该函数会自行撤消。所以在这种情况下,我们只需将第一个字母放在字符串的末尾。

def f4(s): # For when len(s)==4
    #return f3(f3(s[1:])) + s[0] becomes
    #return f3(f3('bcd')) + 'a' becomes
    #return f3('cdb') + 'a' becomes
    #return f3('dbc') + 'a' becomes
    #'dbca'
    return s[3] + s[1:3] + s[0] # swap first and last letter

def f5(s): # For when len(s)==5
    #return f4(f4(s[1:])) + s[0]
    #return f4(f4('bcde')) + 'a'
    #swapping first and last letter twice just swaps then swaps back
    #return 'bcde' + 'a'
    return s[1:] + s[0]

所以看起来我们在这里有一个很好的模式 - 如果字符串有偶数个字母,则交换第一个和最后一个字母。如果它有一个奇数字母,请将第一个字母移到最后!

......不。该模式以f5结尾。如果你使用像'abcd ...'这样的字符串运行函数,你可以很容易地看到每个级别如何移动字母。

f | output
----------
f6|'defbca'
f7|'cdbfgea'
f8|'cgefbhda'
f9|'fecihbgda'

正如你所看到的,对于更长的字符串,除了第一个字符总是在字符串的末尾结束之外,字母被很好地加扰。想到它的最好方法是(用一行代码)你已经设法为每个字符串长度写一个不同的函数(有些函数的行为方式相同,如f3和{{1 }})。每个函数都依赖于它上面的函数,所以因为f5下来很好地随机化了字符串,每个进一步的函数也应该很好地随机化字符串。