Factorに入門する (11) apply combinatorsとcurry
くるくる回りながらも上昇していると信じたいFactorへの入門も11回目です。今回は、apply combinatorsとcurryでFactorがもつパワーの一端を感じてみます。
前回は、"xとyがnを法として合同か?"というwordをこんな風に定義しました。
: modEq? ( x y n -- ? ) swap over mod -rot mod = ;
明らかに分かりづらいですよね。keepというcombinatorを使っても
: modEq? ( x y n -- ? ) [ mod ] keep swapd mod = ;
やはり分かりづらい。トリッキー。スタック操作にあたまを使いすぎている気が。
スタックベースの言語の限界か...? いや、そんなことはないはず。
このwordで本質的にやりたいことはなんなのか。それは"nとのmodをx, yのふたつに適用し、その結果が等しいかどうかを判定する" ことです。"mod nをふたつの値に適用する"ようなことができるcombinatorがあるでしょうか、調べてみましょう。
The apply combinators apply a single quotation to multiple values.
これがつかえそうです。中でも
bi@ ( x y quot -- )
Applies the quotation to x, then to y.
これがまさにそうですね。9と8が2を法として合同か? と判断するなら[ 2 mod ] bi@とやればよいので
( scratchpad ) 9 8 [ 2 mod ] bi@ --- Data stack: 1 ! 9 2 modの結果 0 ! 8 2 modの結果 ( scratchpad ) = -- Data stack: f
というふうにやればよいですね。さて、これをwordにしようとすると、引数としてわたされてくるn (この例でいえば、2)をどうやってquotationの中にいれるか? が問題です。前にとりあげたfried quotationを使ってもいいのですが、よりプリミティブな形であるcurryを使ってみます。
( scratchpad ) 9 8 2 [ mod ] curry --- Data stack: 9 8 [ 2 mod ] ( scratchpad ) bi@ --- Data stack: 1 0
x [ p ] curry で [ x p ] が作られます。Haskellが流行ってからよく見かけるようになった「カリー 化」、スタックベースのFactorではこんなに単純なんです。で、これでmodEq? をかけます。
: modEq? ( x y n -- ? ) [ mod ] curry bi@ = ;
驚異的に分かりやすくなりました。nでmodする関数を作成し([ mod ] curry)、それを2つの値に適用し、それを比較する。ほぼ人間が考えるレベルの抽象度です。fried quotationでも書いてみます。
: modEq? ( x y n -- ? ) '[ _ mod ] bi@ = ;
このくらい単純な例でも、こっちのほうが、読むひとにより易しいですね。