Unity C#でHaskellのmapっぽいものを実装したお話
かずまです。
最近、すごいHaskell楽しく学ぼう! を読み始めました。
おもしろおかしく書かれているところもあるので読み行ってしまいます。
なので、寝る前に少し読む、みたいなスタイルで。
C#でのFunctional的に書く方法
- Lambda式を使う
C#でもLambda式が使えるのでそれを利用します。
無名関数とも言いますね!
公式ドキュメントはこちら。
Lambda Expressions (C# Programming Guide)
- 引数でデリゲートを使う
Lambda式を受けるデリゲートは
Func(T, TResult) Delegate (System)
ココらへんを使えば良さそうです。
** Func
これは一つ目のTは引数の型、TResultは返り値の型をGenericで指定するようです。
なんと、この2つを使えばできるはずですね!!
クラスとメソッドの作成
とりあえず、Funcというクラスを作って、staticでList
このような感じで書いてみました。
メソッドをGenericにするのはうーん、どうなのかな。
ClassをGenericにするのもいい気がするけど今はこっちでかきました。
使い方
先程のクラスとは別にFunctorTestクラスを用意します。
以下のように記述してみました。
public class FunctorTest : MonoBehaviour { private List<int> list = new List<int>(){ 1, 2, 3, 4, 5 }; private void Start () { System.Func<int, int> f = (int arg) => { return arg * 2; }; list = Functor.map (f, list); print (list); } }
追記 2/7
Lambda式をまとめるのもありです。
public class FunctorTest : MonoBehaviour { private List<int> list = new List<int>(){ 1, 2, 3, 4, 5 }; private void Start () { list = Functor.map ((int arg) => {return arg * 2;}, list); print (list); } }
このコードではHaskellと同じように2倍するLambda式を書いて、それをmapの第一引数として渡すだけ!
簡単ですね!ただHaskellと違ってタイプする数は多いのでスマートじゃない...
これぐらいなら許容範囲!
mapはGenericだけど型を指定しなくても良さそう
mapはGenericな型を要求するので実際は
list = Functor.map<int> (f, list);
と指定するのがいいのか、と想定していました。
ところがTestクラスを書いた時にmapの後ろに型の指定を忘れていたのです。
でもコンパイルエラーも出なく、きちんと出力されていたので
自動的に型を認識するのかと感動してしまいました。
きちんとint型だと認識していますね。
公式のドキュメントにもきちんとかいてありました。
Generic Methods (C# Programming Guide)
便利だしHaskellに一歩近づいた?笑
出力結果
素晴らしい、ちゃんと出てますね!
Stringでも試してみよう!
Listのintが出来たのでstringでも同様に使えるはずです。
private List<string> slist = new List<string>() { "Hello", "World" }; private void Start () { System.Func<string, string> fs = (string arg) => { return arg + "(ΦωΦ)"; }; slist = Functor.map (fs, slist); print (slist); }
この様にHelloとWorldの後ろに(ΦωΦ)を付け足すスクリプトです。
結果
にゃーと猫がでてきましたね。
まとめ
Haskellはまだまだ初心者ですので間違ってる解説などありましたらご指摘ください。
あとこう書いたほうがいいよ〜みたいなのもぜひ!