関数合成をいろんな言語で書いてみた。

友人に Java で関数合成するプログラムを作れって課題の相談をされて一緒に考えた。で、せっかくだから勉強も兼ねていろんな言語で書いてみた。Java 以外はほとんど書いたことのない言語。

問題
f(x) = ex
g(x) = x2
h(x) = -x + 1
f⋅g⋅h を Java で表せ。

上のような感じの問題らしい。人伝ひとづて

Java ver. 1

まずは課題でもある Java から。一番手慣れた言語。でも、ラムダ式を扱えないので悩んだ。継承でオーバーライドすることで、関数本体を与えるようにすることで解決。まじめに、なるべく型はインターフェースで。抽象クラスも使って。ブログ書いてる今思うとこんなところまじめにしなくてもいいのに。
目指すは次のような形で書けること。

Function composed = f.compose(g).compose(h);




初めエンクロージングクラスの this 書くの忘れてた。書き方は覚えておれどめったに書かないもん。

C# ver. 1

C# のコードを書いたことなかったのだけど、書いてみたかったので、次は C# で。デリゲートの存在は聞いていたのでうまく使えるかなと。最終的には先の Java のコードと同じようにするが、まずは次のような形で書けることを目指す。

var composed = Function.Compose(Function.Compose(f, g), h);

これなら次の方でいいような気もするけど。

var composed = x => f(g(h(x)));

C# ver. 2

先の Java のように呼べるようにする。プロパティの書き方を知った。

var composed = f.Compose(g).Compose(h);

Java ver. 2

C# 書いてて、そういえば Java ジェネリクス使ってないなと思ったのでジェネリクスを使うように。auto や var に当たるものがないため煩雑。匿名クラスの場合はダイアモンドオペレーター使えないみたい。
メソッドのパラメーターの書く場所がやっぱり忘れる。メソッド名の後じゃ曖昧になるとかで前なんだっけ?


C++ ver. 1

C++11 でラムダ式が入るのね。Visual Studio 2010 なら using namespace std::tr1 で使えるらしい。まず、C# ver. 1 と同じような感じで。auto 使ってもいいね。

function<double(double)> composed = compose(compose(f, g), h);

C++ ver. 2

Function<double, double> composed = f.compose(g).compose(h);

上記のようにいけるように書いたつもりなんだけどコンパイルエラーでうまくいかない。

\microsoft visual studio 10.0\vc\include\xxcallobj(13): error C2064: 1 引数を取り込む関数には評価されません。

cout << composed.apply(3) << endl;

上記のコードの呼び出し先でエラーが出てる。
「1 引数を取り込む関数には評価されません。」があまりにも意味不明だったから調べたら、「term does not evaluate to a function taking 1 arguments」の訳で、「1 引数の関数として項を評価することができません。」といったところだろうか。apply() の形で呼べないと言っているんだろうか。よく分からん。[追記]解決
コンストラクター初期化子を知った。

Haskell

もちろん Haskell だとどんなにシンプルか教えてあげましたよ。2012.7.19追記 ツムジさんのアドバイスのように変更。

続き

C#, C++ の暗黙の型変換や演算子オーバーロードを使うともっとよさそうになりそうなので、次はその辺を調べて書いてみる。あと、C++ で次の2つの違いが分かってないのでそれも調べる。確保する場所のスタックとヒープの違いかな?

Class a(arg);
Class a = new Class(arg);