3.Arithmetic
reactive-banana-bxには例題が用意されている。今回は、その中の一つであるArithmeticについて、そのプログラムの内容を解説する。
Arithmeticは、整数の足し算を実時間で実行してくれる。画面上に二つのエントリーが用意されていて、一つは被加算の整数を、他の一つは加算の整数を入力できるようになっている。数値が入力されると、それらの数の和が瞬時に結果として出力される。もし、整数以外のものが入力された時は、結果は"--"と出力される。
3.1 Graphics.Ui.WX
モジュールGraphics.Ui.WXは、GUIのためのウィジェット(GUIを構成するための部品要素とその集まり)を提供してくれる。主要な部品要素はGUIの枠組み(フレーム)を提供するframeである。フレームには、エントリー、ボタンなどが含まれる。
今回は、二つのエントリーと結果の表示とが必要である。これは、プログラムでは次のように記述する。
f <- frame [text := "Arithmetic"] input1 <- entry f [] input2 <- entry f [] output <- staticText f []
一番最初の行で、フレームを定義し、そのラベルをArithmeticとする。二番目と三番目の行はエントリーの定義である。それらは、input1,input2とする。四行目は出力の表示で、outputとする。
これらは、画面上に配置しなければならないが、それは、次のように行う。
set f [layout := margin 10 $ row 10 [widget input1, label "+", widget input2 , label "=", minsize (sz 40 20) $ widget output]]
これにより、次のような画面が用意される。
3.2 Reactive.Banana.Wx
関数型リアクティブプログラミングは、振舞い(Behavior)とイベント(Event)により、特徴づけられている。ウィジェットで定義された部品要素をBehaviorあるいはEventとして扱えるようにするのが、モジュールReactive.Banana.Wxの役割である。ここでは、このモジュールに含まれている関数の中で、Arithmeticで用いるものについて説明しよう。
behaviorTextは、エントリーを、reactive-bananaのbehaviorとして扱えるようにするものである。
sinkは、アニメーションに似たことを行う。これは、イベントが発生すると、それを振舞いに反映させる。今回説明しているArithmeticでは、キーが押されると、それに応じたエントリーの内容が変化し、それが、また結果にも反映される。
例えば、input1に123という整数が書き込まれていて、今、input2に対して、345という値が入力されたとする。この入力は、3というキーが最初に押されるので、それに反応して、input2には3が書き込まれる。さらにこれに反応して出力が126となる。次に4という数字が入力されるので、input2は34となり、出力は156となる。最後に、5が入力されるので、input2は345となり、出力は458となる。
3.3 Reactive.Banana
それでは、加算がどのように行われるかを調べてみよう。reactive-bananaでは二つの振舞いを合成する関数を用意している。例えば、下の図に示すような二つの振舞いがあったとする。
今、左側の振舞いは、信号の音を調整するもので、入ってきた信号をだんだんに減衰させるものとする。左側の振舞いは信号である。左側の振舞いを、右側の振舞いが要求している強さに変換するためには、下図に示すように、時間ごとに乗算すればよい。
active-bananaでは、振舞い間の乗算を以下のように記述する。
(*)<$>a<*>b
そこで、二つのエントリーからの加算は次のように記述する。
result :: Behavior (Maybe Int) result = f <$> binput1 <*> binput2 where f x y = liftA2 (+) (readNumber x) (readNumber y)
ここで、readNumberはエントリーから数値を読み込んでくる関数である。f x yはそれぞれのエントリーから整数を読み込み、それを加算する。この時、エントリーに値がまだ入っていなかったり、数字以外の文字や記号が入っている場合があるので、出力の方は、Maybe Intで値を得る(Javaなどの例外処理に当たる部分である)。
realNumberの求め方は次のようになっている。
readNumber s = listToMaybe [x | (x,"") <- reads s]
関数readsについての詳細はHaskellの本で調べてください。ここでは、整数が入力された時はxにその値が、そうでないときは、空のリストが返ってくると思ってください。
加算結果は関数showNumberを用いて、結果を得ている場合にはその値を、そうでない場合には"--"で出力する。以下のようになっている。
showNumber = maybe "--" show
3.4 プログラム全体
プログラムは、このホームページにあるが、転載する。
{----------------------------------------------------------------------------- reactive-banana-wx Example: Very simple arithmetic ------------------------------------------------------------------------------} import Data.Maybe import Graphics.UI.WX hiding (Event) import Reactive.Banana import Reactive.Banana.WX {----------------------------------------------------------------------------- Main ------------------------------------------------------------------------------} main :: IO () main = start $ do f <- frame [text := "Arithmetic"] input1 <- entry f [] input2 <- entry f [] output <- staticText f [] set f [layout := margin 10 $ row 10 [widget input1, label "+", widget input2 , label "=", minsize (sz 40 20) $ widget output]] let networkDescription :: MomentIO () networkDescription = do binput1 <- behaviorText input1 "" binput2 <- behaviorText input2 "" let result :: Behavior (Maybe Int) result = f <$> binput1 <*> binput2 where f x y = liftA2 (+) (readNumber x) (readNumber y) readNumber s = listToMaybe [x | (x,"") <- reads s] showNumber = maybe "--" show sink output [text :== showNumber <$> result] network <- compile networkDescription actuate network