[ quoted from ] 情報演習 課題S-B-1(※)一部抜粋・改変

以下に示すような3種類のフラクタル曲線 (コッホ曲線、C曲線、ドラゴン曲線)を描く関数 をそれぞれ定義せよ。
(フラクタルとは)
一般にフラクタルとは、自己相似的な図形のことをいいます。すなわち、図形の任意の部分がその図形全体と相似であるような部分を含むような図形のことをいいます。
フラクタルの例としては、コッホ曲線、ドラゴン曲線、マンデルブロー集合などが有名です。このうち、マンデルブロー集合は、それなりにプログラミングの手間とコンピュータの計算能力とが必要なので、ここではもっと手軽に描けるコッホ曲線などを扱います。
(ジェネレータによるフラクタル曲線)
コッホ曲線などのフラクタル曲線は、ジェネレータを再帰的に適用することによって描くことができます。

問題文だけでは分かりにくいかもしれないので、ベースとなる線分にジェネレータをn回作用させた場合にそれぞれどのような図形が出来上がるのかを下に表で示しておきます。

ジェネレータによるフラクタル生成
段階C曲線ドラゴン曲線コッホ曲線
1
2
3
4
N

それではSchemeでn回繰り返す処理はどう記述するのでしょう。つまり、上で見たようにベースの線分に対してジェネレータを作用させ、それでできた図形のそれぞれの線分にまたジェネレータを作用させ・・・というのをn回すればいいのですが、どうすればいいのでしょう。

これにはいくつか方法がありますが、ここでは関数の再帰的定義を使います。Schemeでは関数の再帰的定義がとても重要です。

(define (n-times x n)
  (if (zero? n)
      1
      (* x (n-times x (- n 1)))
  ))
(n-times 2 6)

この例では、x^nを再帰的に定義しています。前回やったので大丈夫ですね。文字通り、n times 再帰呼び出しされています。ここではnが再帰呼び出しの回数であり、そしてnが0であることが再帰終了条件です。

次のように関数を定義すれば、再帰的にフラクタル曲線を書くことができます。

(define (fractal x1 y1 x2 y2 n)
  (if (zero? n)
      ;[n=0の場合]
      ; ここに最終的な線分描画処理を入れる(x1,y1からx2,y2への線を引く)
      ;[n≠0の場合]
        ;x3,y3などに適当な値(中間点)を計算して代入する。
        ;x1,y1からx3,y3へn-1段階でfractal関数
        ;x3,y3からx2,y2へn-1段階でfractal関数
        ; …… ジェネレータの種類に合わせてここのfractalの指定は変わる
        )
  )

線分描画などの命令は、情報演習ページでSchemeでグラフィックを扱う方法が載っているのでそちらを参考にしてください。n≠0の場合に、ジェネレータが生成する線分の本数に合わせて何回かfractal自身を再帰呼び出しする必要がありますが、これにはbegin命令やlet命令を使います。この場合はlet命令の方がいいでしょう。

また、上の例と本質的に同じことですが、fractal関数の引数を、「(x1,y1)から(x2,y2)へとするのではなく、「(x,y)から(dx,dy)の方向へ」としても同じことができます。コッホ曲線を実装するにはこちらの方が楽かもしれません。

ここで定義される変数は、let文の中でしか使えないローカル変数であることに注意してください。またこの命令は特に、慣れないとカッコの数を間違えやすいので、注意が必要です。

(let ((変数1 値1)
      …
      (変数n 値n)
     )
     (命令1)
     …
     (命令n)
)