簡體   English   中英

在python3上使用遞歸的分形樹

[英]fractal tree using recursion on python3

我不明白遞歸。 main()函數將烏龜對齊。 使用branchLen = 75調用tree()函數。 因此,它通過“ 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()

這不是真的,它沒有向左旋轉。 請注意,總是使用同一只烏龜實例進行繪制,因此,烏龜始終從每次調用前的位置繼續。

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)。

您必須注意,當它branchLen時,它開始使用調用此分支的函數的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)

每次對tree調用都會記住它在哪里。 您是正確的,第一件事是向前和向右轉彎,直到調用tree (0,t) 該調用不滿足if測試,因此不執行任何操作。 但是,這不會影響任何其他tree調用。 因此,回到tree(15,t) ,執行繼續到第6行,並且對於所有其他tree調用類似。

作為練習,您可以嘗試在每個被調用的地方粘貼tree的副本,並為branchLen填寫數字。 每次調用tree ,實際上就是發生的情況。

試試2

想象branchLen是函數名稱的一部分,而不是參數。 您將擁有一系列函數tree75(t)tree60 ,... tree0 tree75()將是:

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以外的所有tree0 ,它什么也不做(相當於在tree tree0 if語句失敗)。 因此,就像任何函數一樣, tree75調用tree60 tree60從其代碼的開頭到結尾運行。 然后tree75從它稱為tree60的位置繼續前進:它右轉並再次調用tree60

就調用和返回的行為而言,每次對遞歸函數的調用都與對任何其他函數的調用一樣。 不同之處在於,您以特定的樣式編寫了遞歸函數,因此在調用自身時有意義。

最好的理解方法是手動調用給定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)
  ...

我有同樣的問題。 我采用了循序漸進的策略。 我畫了一張圖,顯示我如何想象tree(45,t)

鏈接到圖片

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM