(2015/10/1 追記)
以下の記事に書いたlambdaExpr
関数は廃止し、代わりにyield
式 ( yield_0
、yield_1
および yield_2
)を追加した。lambdaExpr
関数は使うのも説明するのも難しいと思っていたのがその理由だ。
yield_n
は関数の評価時にプレースホルダ ph_n
(n = 0,1,2)になるもので、下の記事の例でyield
式を使うとこう書ける。
' ph_1 と ph_2 には評価時に実引数が代入される
' yield_1 は評価時に ph_1 になる
f = p_cons(p_makeSole, p_mapF(p_cons(ph_1, yield_1), ph_2)) '[a] : map (a:) b
m = foldr(p_catV, Array(), scanr(f, Array(), a))
f = p_cons(p_makeSole, p_mapF(lambdaExpr(p_cons, 1, ph_1), ph_2))
よりマシになったと思う。
前回記事 VBAHaskellの紹介 その15(引数の部分文字列のリストを取り出す) で、問題を解くためのアドホックな関数を定義して使ったことを不満点としてあげた。
VBAHaskellで実装している関数合成はスコープがフラットで、すべてのプレースホルダに実引数を渡して一斉に評価することしかできないのが問題で、このままでは本物のラムダ式からはほど遠い。
一応これを解決する関数lambdaExpr
を作ったのでその内容を書く。
(Haskell_1_Core モジュールに追加)
問題にしたのは以下のコードだ。
'consMap 関数の定義は cons 関数を map しているだけ
Function consMap(ByRef a As Variant, ByRef v As Variant) As Variant
consMap = mapF(p_cons(a), v)
End Function
'consMap関数を組み込む
f = p_cons(p_makeSole, p_consMap(ph_1, ph_2)) '[a] : map (a:) b
m = foldr(p_catV, Array(), scanr(f, Array(), a))
ここで p_mapF(p_cons(ph_1), ph_2)
というような形に書ければ、consMap
関数は不要になる。しかし**「すべてのプレースホルダに実引数を渡して一斉に評価することしかできない」**と書いたとおり、これに引数 a, v を渡すと mapF(cons(a, a), v)
もしくは mapF(cons(a, v), v)
などと代入され、先にcons関数が評価されてしまう。本来は mapF(p_cons(a, _), v)
というように、引数を代入した後もプレースホルダを残したファンクタのままでいてほしいのだ。
これは要するに引数の代入に対する関数呼び出しを遅延させればいいので、Haskell_1_CoreモジュールにlambdaExpr
という関数を追加してそれができるようにした。
lambdaExpr
を使うコードはこうなる。1
' p_consを第1引数によって遅延 bind1st する
f = p_cons(p_makeSole, p_mapF(lambdaExpr(p_cons, 1, ph_1), ph_2))
m = foldr(p_catV, Array(), scanr(f, Array(), a))
lambdaExpr
の実質は単なる遅延 bind1st
(またはbind2nd
) である。
上の p_mapF(lambdaExpr(p_cons, 1, ph_1), ph_2)
に引数 a, v を与えると、
mapF(bind1st(p_cons, a), v)
と評価され、最終的に
mapF(p_cons(a, _), v)
という、プレースホルダを残したままの形になっている。
これをラムダ式と呼ぶのは苦しいが lambdaExpr
と名付けた。
VBAHaskellの紹介 その15(引数の部分文字列のリストを取り出す)
VBAHaskellの紹介 その1(最初はmapF)