uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

elmでやってみるシリーズ2: ●を往復させる

import Window
import Debug (log)

-- 旧速度vと、現在位置xから、新しい速度を求める。(はじっこだったら速度を反転する)
newv : Int -> Int -> Int
newv v x = if (x<= -10 || 10<=x) then -v else v

-- 状態を表わす(現在位置、速度)のタプルに対して、次の状態をシグナル(fps 20)のタイミングで累積的に計算しなおすということを繰り返す。初期状態は(0,1)。結果得られるのは、(現在位置,現在の速度)を表現するシグナルである。
pos = foldp (\it (x,v) -> 
            let nv = (newv v (log "x" x))
            in (x+nv, nv)
            ) (0,1) (fps 20)

-- 指定位置x,yを中心に●を書く(半径20の円を書いて赤で塗り潰す)。
drawCircle : Float -> Float -> Form
drawCircle x y = move (x,y) <| filled red (circle 20)

-- 一連の背景線を書く
drawMatrix : [Form]
drawMatrix = map (\x -> drawLine <| x*10) [-10..10]

-- 背景線を1本書く
drawLine : Float -> Form
drawLine x = traced (solid blue) ( segment (x,-100) (x,100) )

-- 「一連の背景線と、●を書いたコラージュを生成する純粋関数f」に、現在位置(posタプルの第一要素)をシグナル化したまま取り出してリフト適用
main : Signal Element
main=let f = \w h p-> collage w h ([drawCircle ((toFloat p)*10) 10]++drawMatrix)
     in f <~ Window.width ~ Window.height ~ (fst <~ pos)


解説的な何か

  • foldpのpは、pastのpで、その意味は実際に「過去から現在までのシグナルの履歴値を畳み込む」です。しかし、foldp使用に伴なうオーダーは実行開始時刻から現在までの時間経過には依存せず、O(1)です。実際には、シグナルの更新1回ごとに1回呼び出される関数を登録するというものです。foldpの第一引数の関数の第二引数(上で言う(x,v))は前回のfoldpの返り値を保持しています。この値が、いわゆる状態を保持・更新するためのelmにおいての唯一の手段です。
  • Debug.logは、JavaScriptでいうConsole.logを呼び出します。正格評価だからその時点の値が普通に出ます。

続く。

関連エントリ


「Elmでやってみる」シリーズのまとめエントリ - uehaj's blog