(2016/11/16 追記)
プレースホルダの扱いを大きく変更しました。yield式はなくなってはいませんが、明示的に書く必要はほとんどなくなり、より直感的にプログラムできるようになりました。
VBAHaskellに実装していた lambdaExpr
という関数を廃止し、代わりにyield式 ( yield_0
、yield_1
および yield_2
)を導入した。その旨を VBAHaskellの紹介 その16(ラムダ式?の生成)に追記している。
lambdaExpr
はいったん作ったものの、使うのも説明するのも難しく、実際使えなかった。実体が「遅延バインド」というややこしいもので、そのうえ表現したいHaskellの式との対応関係も見えづらいものだったからだ。
当初の問題意識
- 関数の入れ子が関数合成でしかない
配列 m に対してmapF(p_plus(1), m)
を計算すると、m の各要素に操作1+
がマップされる。この場合はmapF
にp_plus
を引数として渡していると言える。mapF
は渡されたplus
を実行時に「関数として」使うように実装されている。
これに対してp_plus(p_plus(p_plus( ... ))
のような式は単に足し算を合成しているので、実行時には関数に関数を渡すのではない。外側の関数は内側の関数が計算した「結果の値」を使うだけだ。 - 関数の実行を遅延させたい
式p_Func1(p_Func2(1, ph_1), ph_2)
に引数 a, b を渡すと、Func1(Func2(1, a), b)
という計算が実行される。しかしそうでなく、何らかの式を評価した結果がFunc1(p_Func2(a, ph_1), b))
のような 関数に関数を渡した形 になってほしいことがある。内側の関数が実行されずに関数のまま残っているイメージだ。(ph_0
、ph_1
、ph_2
はVBAHaskellで用意したプレースホルダ式)
評価時にプレースホルダになる式
実行したときに内側の関数が関数のまま残り、プレースホルダもそのまま残っていてほしいが、VBAHaskellの関数合成はスコープがフラットで、すべてのプレースホルダに実引数を渡して一斉に評価することしかできない。
そこで「評価するとプレースホルダに変化する式」として以下の3種類の yield式 を導入することにした。
・ yield_0
→ ph_0
・ yield_1
→ ph_1
・ yield_2
→ ph_2
そして「ひとつ以上の引数がyield式になっている関数式は、評価後も関数式のまま残す」というルールとし、API側でそうなるよう実装した。こうすれば、式 p_Func1(p_Func2(ph_1, yield_1), ph_2)
に引数 a, b を渡すと、Func1(p_Func2(a, ph_1), b))
という形になる。
Haskellの式との対応
Haskellのこういう式を考える。
\a b -> [a] : map (a:) b
(a: ) のところにプレースホルダを置いて、疑似的に
\a b -> [a] : map (a: _placeholder_ ) b
という式だと考え、関数や式を以下のように対応させる。
: 演算子 → cons 関数
a → ph_1(ラムダ式の仮引数はプレースホルダ)
b → ph_2(ラムダ式の仮引数はプレースホルダ)
_placeholder_ → yield_1
[ a ] → makeSole 関数
これによってVBAHaskellとしては次の式となる。
p_cons(p_makeSole, p_mapF(p_cons(ph_1, yield_1), ph_2))
長いしカッコ悪いが、いちおう対応はしている。
C++だとこんなラムダ式になるだろう。(効率とかは無視している)
[]<typename T>(T const& a, std::vector<std::vector<T>> const& b){
auto tmp = b;
for ( auto& i : tmp ) i.insert(i.begin(), a);
tmp.insert(tmp.begin(), std::vector<T>{1, a});
return tmp;
};
これを含むサンプルは テストモジュール の Sub segmentsTest2 にある。
ところで上のC++14のラムダ式はgccだといけるけど、clangだとダメ。規格的にはどうなんでしょう。
http://melpon.org/wandbox/permlink/04WzozqwuAerr0Ps
それと、
[ ]<typename T, template <typename> class V>(T const& a, V<V<T>> const& b)
って書き方できないんですかね?
過去記事
VBAHaskellの紹介 その16(ラムダ式?の生成)
VBAHaskellの紹介 その23(ジャグ配列をフラットな配列に展開する再帰)
VBAHaskellの紹介 その1 (最初はmapF)