くるくる回りながらも上昇していると信じたい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があるでしょうか、調べてみましょう。

Apply combinators

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@ = ;

このくらい単純な例でも、こっちのほうが、読むひとにより易しいですね。