2015-06-19 84 views
5

这个功能对我来说没有意义。我已经在各地添加了打印语句来弄清楚发生了什么,我仍然没有得到它。如果有人能向我解释这一点,我会很感激。双递归教育示例

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(“at”)的字符串,它再次绕过if语句并进入f(s [1]),这给了我们最后进入if语句的长度为1(“t”)的字符串并返回“T”。这是对我来说,这条线索寒冷的地方。

从我的打印语句中,我看到一个长度为2的新字符串被创建并且随后的“a”被返回。最终产品最终成为“atm”。由于“+ s [0]”部分,我得到了“m”标签,但为什么它是“atm”而不是“tam”?

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

+1

您是否尝试过使用笔和纸?它不是非常复杂,所以我建议你用变量的当前状态绘制你的调用堆栈。你最好自己找出来,而不是有人向你解释。 – amza

+1

@stazima - 因为他已经花了几个小时,所以我认为OP会更好,并且更清楚地解释它,否则他最终会撞到他迄今为止打的墙。 – rmunn

回答

1

短版:at越来越交换两次,所以内f("at")调用返回"ta",然后外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")f2("ta")的值,看看是否可以发现发生了什么。如果您在15-20分钟的工作后仍然需要帮助,请对此答案发表评论,我会再详细介绍一下。

+0

我可以看到你要去的地方,但它仍然没有发生在我身上。我的挂断与s [0] =“a”然后等于“t”的事实有关。所以第一个循环返回“t”+ s [0](“a”)=“ta”。然后它再次通过并反转这两个。但我不明白为什么它会经历第二次,然后为什么s [0]再次变成“m”。 – futevolei

+1

's [0]'不是一回事,它存在于函数内部,但每次调用f()'时它都存在一次*,并且它只在给定调用*中存在。由于'f()'被多次调用,因此在整个程序运行过程中会创建许多's [0]'版本。它不会“再次变为m”,它在不同的f()调用中是不同的s [0]。 – TessellatingHeckler

+0

@futevolei您提到发生了什么“循环”这一事实,表明您在考虑如何考虑这个问题时存在一个基本问题。没有循环。只有函数调用。如果您知道如何跟踪函数调用中发生的情况,那么函数调用另一个函数(本身)不应该影响您跟踪整体情况的能力。如果你认为这是一个循环,所有的投注都关闭。 – pjs

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. 
+0

为什么要两次打电话给f?它是不是在第一次调用返回的值时调用f,然后f? – stenci

+0

@stenci它的确如此,这就是我所说的“通话两次”。也许这不是一个很好的描述方式......我已经对它进行了一些修改。 – TessellatingHeckler

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' 

因此,大家可以看到,对于更长的串字母加扰相当周围以及比其他第一个字符总是在字符串的结尾结束了。想想最好的方法是(使用一行代码),您已经设法为每个字符串长度编写不同的函数(一些函数的行为方式相同,如f3f5)。每个函数都依赖于它上面的函数,所以因为f6就很好地随机化了字符串,所以每个函数都应该随机化字符串。