5.Counter
振舞いについては、前の二つの記事で、テキストフィールドを用いて説明した。今回は、reactive programmingのもう一つの一翼を担うイベントについて説明する。
この記事でのプログラムのタイトルは、Counterである。upとdownのボタンがあり、upが押されると一つ増やされ、downが押されると一つ減らされるcounterがあるとする。counterは押された数で変化する振舞いとなり、upとdownはイベントとなる。それでは、このプログラムを見ることにしよう。初期画面は次のようになる。
5.1 ユーザ・インタフェース
ユーザ・インタフェースは、これまでのフレームと結果表示のテキストフィールドに付け加えて、upとdownのための二つのボタンを用意する。次のようになる。
f <- frame [text := "Counter"] bup <- button f [text := "Up"] bdown <- button f [text := "Down"] output <- staticText f [] set f [layout := margin 10 $ column 5 [widget bup, widget bdown, widget output]]
なお、buttonはモジュールGraphics.UI.WXで定義されている。
5.2 イベント
ボタンはイベントとして次のように表される。
eup <- event0 bup command edown <- event0 bdown command
上記のプログラムで、event0はモジュールReactive.Banana.WXが用意した関数で、ウィジェットとコマンドを引数にとる。このプログラムでは、ウィジェットはボタンである。コマンドのところはcommandと書く。これはウィジェットに対するコマンドと解釈される。ここでは、ボタンがウィジェットなので、コマンドはボタンを押すことである。
5.3 振舞いとイベントのAPI
upのボタンが押されたときに1増やすイベントは次のようになる(<$はfmap . constの中置関数である。これについては次の記事で詳しく説明する)。
(+1) <$ eup
downに対しては次のようになる。
subtract 1 <$ edown
二つのイベントは、関数unionsにより集められる。
unions [ (+1) <$ eup, subtract 1 <$ edown]
上記を時間軸に沿って集積したものは、関数accumBを用いて、次のように記述できる。
accumB 0 $ unions [ (+1) <$ eup, subtract 1 <$ edown]
この値が、counterのそれとなるので、次のように記述する。
(counter :: Behavior Int) <- accumB 0 $ unions [ (+1) <$ eup , subtract 1 <$ edown ]
unionsもaccumBもReactive.Banana.Combinatorsで用意されている。
5.4 アニメーションとして表示する
counterの値を示すには、例によって、アニメーション化すればよいので次のようにする。
sink output [text :== show <$> counter]
5.5 プログラム全体
プログラムはこのホームページで公表されているが、転載する。
{----------------------------------------------------------------------------- reactive-banana-wx Example: Counter ------------------------------------------------------------------------------} {-# LANGUAGE ScopedTypeVariables #-} -- allows pattern signatures like -- do -- (b :: Behavior Int) <- stepper 0 ... import Graphics.UI.WX hiding (Event) import Reactive.Banana import Reactive.Banana.WX {----------------------------------------------------------------------------- Main ------------------------------------------------------------------------------} main :: IO () main = start $ do f <- frame [text := "Counter"] bup <- button f [text := "Up"] bdown <- button f [text := "Down"] output <- staticText f [] set f [layout := margin 10 $ column 5 [widget bup, widget bdown, widget output]] let networkDescription :: MomentIO () networkDescription = do eup <- event0 bup command edown <- event0 bdown command (counter :: Behavior Int) <- accumB 0 $ unions [ (+1) <$ eup , subtract 1 <$ edown ] sink output [text :== show <$> counter] network <- compile networkDescription actuate network