「プログラムでシダを描画する」をelmで描画する
やや乗り遅れているネタとして、シダを描くというのを、elm言語でやってみました。
(追記: 改良版も作りました)
elm言語は、基本はHaskellライク文法(サブセット方向)に、F#とOCaml風味の演算子・文法を振り掛けた、ヒンドリーミルナー型推論・純粋関数型・正格評価の言語で、repl上もしくは主にJSにコンパイルしてブラウザ内で実行します*1。特徴はFRP,ファンクショナルリアクティブプログラミングをサポートする言語だということです。
以下がシダを描画するelmコード。もっといい書き方あると思うので気付いたらご指摘お願いします。
import Mouse import Generator import Generator.Standard sida_width=500 sida_height=500 randomSeed = 12346789 gen = Generator.Standard.generator randomSeed main = collage (sida_width*2) (sida_height*2) <~ ((f0 13 0 0 gen) <~ (flip (/) sida_width <~ (toFloat <~ Mouse.x))) -- Original: w1x x y = 0.836 * x + 0.044 * y w1x x y n = n * x + (1-n) * y w1y x y = -0.044 * x + 0.836 * y + 0.169 w2x x y = -0.141 * x + 0.302 * y w2y x y = 0.302 * x + 0.141 * y + 0.127 w3x x y = 0.141 * x - 0.302 * y w3y x y = 0.302 * x + 0.141 * y + 0.169 w4x x y = 0 w4y x y = 0.175337 * y f0 k x y gen0 n = let (seq, _)=f k x y gen0 n in [(move (0,0)) (filled black <| rect (sida_width*2) (sida_height*2)) ]++seq f k x y gen0 n = if (0 < k) then let (rnd1, gen1) = Generator.int32 gen0 (seq1, gen2) = (f (k - 1) (w1x x y n) (w1y x y) gen1 n) (seq2, gen3) = if (rnd1 `mod` 3 == 0) then (f (k - 1) (w2x x y) (w2y x y) gen2 n) else ([], gen2) (seq3, gen4) = if (rnd1 `mod` 3 == 1) then (f (k - 1) (w3x x y) (w3y x y) gen3 n) else ([], gen3) (seq4, gen5) = if (rnd1 `mod` 3 == 2) then (f (k - 1) (w4x x y) (w4y x y) gen4 n) else ([], gen4) in (seq1 ++ seq2 ++ seq3 ++ seq4, gen5) else ((plot (x * sida_width * 0.98) (y * sida_height * 0.98)), gen0) plot x y = [move (x,y) (filled green <| rect 1 1)]
リアクティブプログラミングというのは、私の理解では、時間経過とかマウスクリックとか、なんらかの(外部/外部)イベントに刻々と反応することの記述を容易にできるってことです(雑すぎる説明)。それがファンクショナルなのは、関数型ってことで、elmはさらにピュア・ファンクショナルなので副作用は記述できません。Signalというのが「変化する値」を表わしていて、これはHaskellのIOアクションが副作用を一手に背負っているようなものです。SignalはしかしモナドではなくArrowで、これでFRPを実現しているのを Arrowized FRPと呼ぶとか。ただそこらへんの詳しいことはわからなくても書けます*2。
上記のコードは、マウスのx座標で反応するようにしました。FRPなのはmainだけであとは全部ピュアな関数です。x座標に対応して、シダの図のどのパラメータをどう動かすかは、適当にやったので動きは不自然です。あと再帰の深さを深くすると非常に遅くなります。Canvasに書き捨ててるんじゃなくて、描画命令(Form,形状)をリストで持ってるからです。
以下にはコンパイル結果のJSを登録しましたので、ブラウザ上で実際に動かして試すことができます。マウスを左右に動くとなんか動くわけです。
以下は、静止画像のスナップショットです(なので動きません)。
枝ぶりが貧弱なのは、速度上の問題ですorz。
以下はelm_dependencies.jsonの内容。標準関数以外には、random generatorというのを使っています。
{ "version": "0.1", "summary": "concise, helpful summary of your project", "description": "full description of this project, describe your use case", "license": "BSD3", "exposed-modules": [], "elm-version": "0.12.3", "dependencies": { "jcollard/generator": "0.3" }, "repository": "https://github.com/USER/PROJECT.git" }
elmについては別途紹介記事を書くつもり。