在python3上使用递归的分形树

时间:2017-07-18 10:05:18

标签: python recursion tree fractals

我无法理解递归。 main()函数对齐乌龟。使用tree()调用branchLen = 75函数。因此,它通过“if”条件并上升。根据我的理解,乌龟应该连续5次右转,其长度减少为75,60,45,30,15。此后,它将不再满足“if”条件。代码只运行到第5行(第一次递归调用)。因此,应显示倾向于RHS的单行。不应该有任何左转。 但这不会发生,会形成一个完整的对称树。 请解释一下。
有关问题的详细信息,请参阅链接 谢谢!
https://interactivepython.org/runestone/static/pythonds/Recursion/pythondsintro-VisualizingRecursion.html

def tree(branchLen,t):
    if branchLen > 5:
        t.forward(branchLen)
        t.right(20)
        tree(branchLen-15,t)
        t.left(40)
        tree(branchLen-15,t)
        t.right(20)
        t.backward(branchLen)

def main():
    t = turtle.Turtle()
    myWin = turtle.Screen()
    t.left(90)
    t.up()
    t.backward(100)
    t.down()
    t.color("green")
    tree(75,t)
    myWin.exitonclick()

main()

4 个答案:

答案 0 :(得分:1)

不是这样,它不会向左转。注意乌龟的相同实例总是用于绘制,所以乌龟总是从每次调用之前的位置继续。

def tree(branchLen,t, direction="straight"):
    if branchLen > 5:
        print branchLen, t.pos(), direction #debug info
        t.forward(branchLen)  #go forward branchlen
        t.right(20)           #rotate right 20
        tree(branchLen-15,t)  #call first branch of recursion
        t.left(40)            #rotate left 40 
        tree(branchLen-15,t)  #call second branch of recursion
        t.right(20)           #rotate right 20
        t.backward(branchLen) #go back branchlen - it is now reset to the original position before this call of tree
        print "reset to previous", t.pos()

所以基本上发生的是:

  1. 它会像你期望的一样向右移动 - 这是因为它向右旋转,然后它进入递归的第一个分支
  2. 它从这个分支开始浮出水面,这意味着向后移动到每个级别,然后调用第二个分支(这将使其在重新铺设之前再次前进,从而重复步骤1和2)< / LI>

    你必须注意,当它表面时,它会使用调用此分支的函数的branchLen值开始,而t保持不变。这是调试信息的结果:

    branchlen, position, direction called
    75 (-0.00,-100.00) straight
    60 (-0.00,-25.00) right
    45 (20.52,31.38) right
    30 (49.45,65.85) right
    15 (75.43,80.85) right
    0 (90.20,83.46) right # 0 does not draw
    0 (90.20,83.46) left
    reset to previous (75.43,80.85) #after this it will resurface 1 level, and repeat
    15 (75.43,80.85) left
    0 (85.07,92.34) right
    0 (85.07,92.34) left
    reset to previous (75.43,80.85)
    reset to previous (49.45,65.85) #here it resurfaces twice
    30 (49.45,65.85) left
    15 (59.71,94.04) right
    0 (69.35,105.54) right
    0 (69.35,105.54) left
    reset to previous (59.71,94.04)
    15 (59.71,94.04) left
    0 (59.71,109.04) right
    0 (59.71,109.04) left
    reset to previous (59.71,94.04)
    reset to previous (49.45,65.85)
    reset to previous (20.52,31.38)
    45 (20.52,31.38) left
    30 (20.52,76.38) right
    15 (30.78,104.57) right
    0 (40.42,116.06) right
    0 (40.42,116.06) left
    reset to previous (30.78,104.57)
    15 (30.78,104.57) left
    0 (30.78,119.57) right
    0 (30.78,119.57) left
    reset to previous (30.78,104.57)
    reset to previous (20.52,76.38)
    30 (20.52,76.38) left
    15 (10.26,104.57) right
    0 (10.26,119.57) right
    0 (10.26,119.57) left
    reset to previous (10.26,104.57)
    15 (10.26,104.57) left
    0 (0.62,116.06) right
    0 (0.62,116.06) left
    reset to previous (10.26,104.57)
    reset to previous (20.52,76.38)
    reset to previous (20.52,31.38)
    reset to previous (0.00,-25.00)
    60 (0.00,-25.00) left
    45 (-20.52,31.38) right
    30 (-20.52,76.38) right
    15 (-10.26,104.57) right
    0 (-0.62,116.06) right
    0 (-0.62,116.06) left
    reset to previous (-10.26,104.57)
    15 (-10.26,104.57) left
    0 (-10.26,119.57) right
    0 (-10.26,119.57) left
    reset to previous (-10.26,104.57)
    reset to previous (-20.52,76.38)
    30 (-20.52,76.38) left
    15 (-30.78,104.57) right
    0 (-30.78,119.57) right
    0 (-30.78,119.57) left
    reset to previous (-30.78,104.57)
    15 (-30.78,104.57) left
    0 (-40.42,116.06) right
    0 (-40.42,116.06) left
    reset to previous (-30.78,104.57)
    reset to previous (-20.52,76.38)
    reset to previous (-20.52,31.38)
    45 (-20.52,31.38) left
    30 (-49.45,65.85) right
    15 (-59.71,94.04) right
    0 (-59.71,109.04) right
    0 (-59.71,109.04) left
    reset to previous (-59.71,94.04)
    15 (-59.71,94.04) left
    0 (-69.35,105.54) right
    0 (-69.35,105.54) left
    reset to previous (-59.71,94.04)
    reset to previous (-49.45,65.85)
    30 (-49.45,65.85) left
    15 (-75.43,80.85) right
    0 (-85.07,92.34) right
    0 (-85.07,92.34) left
    reset to previous (-75.43,80.85)
    15 (-75.43,80.85) left
    0 (-90.20,83.46) right
    0 (-90.20,83.46) left
    reset to previous (-75.43,80.85)
    reset to previous (-49.45,65.85)
    reset to previous (-20.52,31.38)
    reset to previous (0.00,-25.00)
    reset to previous (0.00,-100.00)
    

答案 1 :(得分:0)

每次拨打tree都会记住它的位置。你是正确的,首先要发生的是前向和右转的链,直到tree (0,t)被调用。该调用不满足if测试,因此不执行任何操作。但是,这不会影响任何其他tree调用。因此,回到tree(15,t),继续执行第6行,对所有其他tree调用也是如此。

作为练习,您可以尝试粘贴一个tree每个地方的副本,并填写branchLen的数字。每次调用tree时,都会发生这种情况。

尝试2

想象一下branchLen是函数名称的一部分,而不是参数。您将拥有一系列函数tree75(t)tree60,... tree0tree75()将是:

def tree75(t):
    # don't need an if statement since we know 75>5
    t.forward(75)
    t.right(20)
    tree60(t)     # <-- 75-15 = 60.  Direct call to tree60().
    t.left(40)
    tree60(t)     # ditto
    t.right(20)
    t.backward(branchLen)

并且类似于除tree0之外的所有内容,它们什么都不做(相当于if中的tree语句失败)。就像任何函数一样,tree75调用tree60tree60从代码的开头到结尾运行。然后tree75继续从其调用tree60的位置开始:它向右转并再次调用tree60

每次调用递归函数就像调用任何其他函数一样,就调用和返回的行为而言。不同之处在于您以特定样式编写递归函数,以便在调用自身时有意义。

答案 2 :(得分:0)

理解的最佳方法是手动跟踪您拨打tree(20,t)的代码。

您应该发现第一次输入tree()时条件满足,但在两次递归呼叫期间,条件不满意,递归呼叫立即返回其呼叫站点并继续tree()函数的其余部分一起使用。

要手动跟踪代码,您应该使用笔和纸写下每个执行的语句,但是当您到达tree()的递归调用时,您应该继续写下语句,但缩进它们:

tree(20,t)
  if branchLen>5
  t.forward
  t.right(20)
  tree(5,t)   <--- recursive call, so start indenting next line
    if branchLen>5  <--- if fails, so return and unindent
  t.left(40)
  ...

答案 3 :(得分:0)

我有同样的问题。我采用了循序渐进的策略。我画了一张图片,展示了我对tree(45,t)的这一过程的想法:

link to image