連想リストを適用可能にするために必要なこと

連想リストから値を検索する際に、このように lookup 関数を毎度毎度呼び出すのが面倒だなーと思うことがあります。

(let ((al '((a . 1) (b . 2) (c . 3))))
  (list (lookup 'a al) (lookup 'b al) (lookup 'c al)))

これを、

(al 'a)

のように書けたら楽ですよね。(特に、データの提供者と利用者とを分離して、データを集めて提供する側でこのように適用可能な形にして返してくれるのが理想的だと思います。)

ここで、キーとオブジェクトの順序が lookup の呼び出しとは逆になっていることに着目すると、

(lambda (al) (lambda (k) (lookup k al)))

lookup の上に、引数を逆順に受け取るようにして lambda を被せてやれば良いことが分かります。

この操作を一般化したものを Haskell では flip と呼びます。

定義

(fun (flip f x y) (f y x))

実行例

> (let ((obj (flip lookup '((a . 1) (b . 2) (c . 3)))))
    (list (obj 'a) (obj 'b) (obj 'c)))
(1 2 3)

他のものでも試してみましょう。

例えばリストですが、list-ref は語順が lookup と逆なので、flip で適用可能にすることはできません。

でも、flip の2乗ならば?

> (define flip^2 (compose flip flip))
> (let ((obj ((flip^2 list-ref) '(a b c))))
    (list (obj 0) (obj 1) (obj 2)))
(a b c)

できました。vector-ref や string-ref 等でも同様です。

fun マクロによって flip が (ひいては f が) 部分適用可能になることで、適用可能なオブジェクトが自動的に作られている、というのが今回の要点です。

いずれにせよ2引数関数にしか使えない技ですが。