Pythonのタートル・グラフィックス(4):クローンkameなしの複雑再帰

先のkameの複雑な再帰描画をクローンを使わないで行うことを考える。まず問題の所在を明確にするためにプログラムにデバッグ用のprint文を追加した。プログラムの一部を載せる(再帰の深さは浅くしてある)。

def kame( position, size):
space = ' '*4*(int(200/size)-1)
print(space, 'draw kame',position.ycor(), size)
sugata( position, size)
if size > 75:
position.fd(size/2)
kame(position, size/2)
position.bk(size)
kame(position, size/2)
print(space, 'returning', position.ycor(), size)

この出力は以下のようになる:


 draw kame 0.0 200
     draw kame 100.0 100.0
             draw kame 150.0 50.0
             returning 150.0 50.0
             draw kame 50.0 50.0
             returning 50.0 50.0
     returning 50.0 100.0
     draw kame -150.0 100.0
             draw kame -100.0 50.0
             returning -100.0 50.0
             draw kame -200.0 50.0
             returning -200.0 50.0
     returning -200.0 100.0
 returning -200.0 200

draw kame 100に対応する(再帰のレベルが同じ)描画がdraw kame -150となっておりこれがずれの原因になる。

そこで関数kameに渡したオブジェクトpositionの属性がこのkame関数のなかで最終的に変化がなかったようにダミーの動きをいれる。プログラムは以下のようになる。


#coding: utf-8

import turtle

def sugata( center, size):
    for i in range(6):
        painter = center.clone()
        painter.fd(size)
        painter.rt(120)
        painter.fd(size)
        center.rt(60)

def kame( position, size):
    #space = ' '*4*(int(200/size)-1)
    #print(space, 'draw kame',position.ycor(), size)
    sugata( position, size)
    if size > 10:
        position.fd(size/2)
        kame(position, size/2)
        position.bk(size)
        kame(position, size/2)
        position.fd(size/2) #ダミーの動き
    #print(space, 'returning', position.ycor(), size)

    
if __name__ == '__main__':
    
    turtle.clearscreen()
    position = turtle.Turtle()
    position.ht()
    position.lt(90)
    kame( position, 200)

【結果の描画」

クローンkameなしの再帰描画の完成図

期待した通りの結果が得られた。

 

Pythonのタートル・グラフィックス(3):クローンkameによる複雑再帰

「Turbo Graphics」(安齋利洋・伊吹龍著;1987年)にはもう少し複雑なkameの再帰描画の例が載っている。プログラムをなぞってPythonで書くと以下のようになる:


#coding: utf-8

import turtle

def sugata( center, size):
    for i in range(6):
        painter = center.clone()
        painter.fd(size)
        painter.rt(120)
        painter.fd(size)
        center.rt(60)

def kame( position, size):
    sugata( position, size)
    if size > 10:
        position.fd(size/2)
        kame(position, size/2)
        position.bk(size)
        kame(position, size/2)

    
if __name__ == '__main__':
    
    turtle.clearscreen()
    position = turtle.Turtle()
    position.ht()
    position.lt(90)
    kame( position, 200)

描画は以下のようになる:

kameの再帰描画

ご覧のように再帰の描画がずれたものになっている。

このずれの原因として考えられるものはオブジェクトを関数に渡す仕様がTurboPascalとPythonとで異なることにあるように思われた。そこで

def kame( positionT, size):
position = positionT.clone()

として関数の中で実引数が影響を受けないようにした。結果の描画は以下のようになる:

kameの再帰描画

これで期待した通りになったが、描画のアルゴリズムを変えてクローンを使わないことにしたい。

Pythonのタートル・グラフィックス(2):kameの単純再帰描画

Pythonのタートル・グラフィックス(1)で採り上げたKameの単純な再帰プログラムを考える。プログラムは以下のようなものである:


#coding: utf-8

import turtle

def sugata( center, size):
    for i in range(6):
        painter = center.clone()
        painter.fd(size)
        painter.rt(120)
        painter.fd(size)
        center.rt(60)

def kame( position, size):
    sugata( position, size)
    if size > 10:
        kame(position, size/2)
    
if __name__ == '__main__':
    
    turtle.clearscreen()
    position = turtle.Turtle()
    position.ht()
    position.lt(90)
    kame( position, 200)

実行結果の描画

kame2の描画

期待した結果である。