LoginSignup
0
0

More than 5 years have passed since last update.

VBAHaskellの紹介 その24(yield式の導入~lambdaExprの廃止)

Last updated at Posted at 2015-10-02

(2016/11/16 追記)
プレースホルダの扱いを大きく変更しました。yield式はなくなってはいませんが、明示的に書く必要はほとんどなくなり、より直感的にプログラムできるようになりました。


VBAHaskellに実装していた lambdaExprという関数を廃止し、代わりにyield式 ( yield_0yield_1 および yield_2 )を導入した。その旨を VBAHaskellの紹介 その16(ラムダ式?の生成)に追記している。
lambdaExprはいったん作ったものの、使うのも説明するのも難しく、実際使えなかった。実体が「遅延バインド」というややこしいもので、そのうえ表現したいHaskellの式との対応関係も見えづらいものだったからだ。

当初の問題意識

  1. 関数の入れ子が関数合成でしかない
    配列 m に対して mapF(p_plus(1), m) を計算すると、m の各要素に操作 1+ がマップされる。この場合は mapFp_plus を引数として渡していると言える。mapF は渡された plus を実行時に「関数として」使うように実装されている。
    これに対して p_plus(p_plus(p_plus( ... )) のような式は単に足し算を合成しているので、実行時には関数に関数を渡すのではない。外側の関数は内側の関数が計算した「結果の値」を使うだけだ。
  2. 関数の実行を遅延させたい
    p_Func1(p_Func2(1, ph_1), ph_2) に引数 a, b を渡すと、Func1(Func2(1, a), b) という計算が実行される。しかしそうでなく、何らかの式を評価した結果が Func1(p_Func2(a, ph_1), b)) のような 関数に関数を渡した形 になってほしいことがある。内側の関数が実行されずに関数のまま残っているイメージだ。(ph_0ph_1ph_2 はVBAHaskellで用意したプレースホルダ式)

評価時にプレースホルダになる式

実行したときに内側の関数が関数のまま残り、プレースホルダもそのまま残っていてほしいが、VBAHaskellの関数合成はスコープがフラットで、すべてのプレースホルダに実引数を渡して一斉に評価することしかできない。
そこで「評価するとプレースホルダに変化する式」として以下の3種類の yield式 を導入することにした。
    ・ yield_0ph_0
    ・ yield_1ph_1
    ・ yield_2ph_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)

0
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0