osiire’s blog

ふしぎなそふとやさん

ラジオボタンの相互依存をFRPで表現してみる

ラジオボタンが二つあったとしますと、片方が選択されればもう片方の選択は解除され、逆も然りです。このような相互依存的なロジックをFRP的なプログラムで表現してみましょう。(もちろんconcurrent cellで。)
下のプログラムは、jキーが押されるとradio_button1は自動的にtrue、radio_button2はfalseになる仕掛けになっています。kキーが押されれば、逆にradio_button1はfalse, radio_button2はtrueになります。

module R = Ccell.Frp.TimeVaryReact (* concurrent cellのモジュール *)
module E = Ccell.Frp.TimeVaryEvent (* concurrent cellのモジュール *)

let key_event, sender = (* イベント作成 *)
  E.make ()

let press_j = (* jキーが押されたイベント *)
  key_event +> E.filter (fun c -> c = 'j')

let press_k = (* kキーが押されたイベント *)
  key_event +> E.filter (fun c -> c = 'k')

let map_true e = (* trueに変換 *)
  E.map (fun _ -> true) e

let map_false e = (* falseに変換 *)
  E.map (fun _ -> false) e

(*ラジオボタンが押されている状態かどうかをboolで表現する値 *)
let radio_button1 = 
  let new_flag =
    (* 新しい状態を決定 *)
    E.merge (map_true press_j) (map_false press_k)
  in
  (* 初期値trueにイベントを関連付け *)
  E.react true new_flag

let radio_button2 =
  let new_flag =
    (* press_jとpress_kが上のコードとは逆になっている *)
    E.merge (map_true press_k) (map_false press_j)
  in
  (* 初期値falseにイベントを関連付け *)
  E.react false new_flag

こういう相互依存的なプログラムがGUIにはよく登場します。しかも取扱いが意外と難しいです。それに対して、FRPなら結構すっきりと書けます。素晴らしい。
完全なコードが見たい方は、https://forge.ocamlcore.org/scm/viewvc.php/trunk/example/radiob.ml?view=markup&root=ccellに置いてありますのでクリックあれ。