ラムダ式への道 その2
匿名メソッドまたは匿名関数
C# 2.0からdelegate定義をしなくてもその場限りで使用するメソッド(関数)を作成できるようになりました。
それを匿名メソッド(匿名関数)と呼んでいます。
サンプル
前回のプログラムを匿名メソッドを利用して記述するとこんな感じになります。
こんな感じに
delegate string SomeFunction(int value); class SomeClass { private SomeFunction _someFunction; public SomeClass(SomeFunction function) { _someFunction = function; } public string DoSomething(int value) { return _someFunction(value); } } class Program { static void Main(string[] args) { var some = new SomeClass(delegate (int v) { return (v * v).ToString(); }); var value = 10; Console.WriteLine(some.DoSomething(value)); } }
SomeClassのコンストラクタ内に直接メソッドを記述しました。なので前回作成したDoSquareメソッドがなくなっています。
ここまでのまとめ
その場限りなのに関わらず、都度プライベートメソッドを作成することが必要でしたが不要になったことがメリットです。このことがのちのバージョンに効いてきます。
次回
次回こそはラムダ式そのものについて書きます。
お詫び
最近誤字が多くなってきている、済みません。
注意力が落ちているのが実感できます、年なのか?
ラムダ式への道 その1
ラムダ式って何か
「ラムダ式って何?」 日本語でうまく翻訳することができません。なので聞かれても「ラムダ式はラムダ式」って答えてしまいます。
他の人はどうやって説明するのかなって思っていつものようにウェブで調べてみてもやっぱりうまい具合に簡潔な説明なものって見つけられません。
なのでラムダ式に至るまでに必要な知識を段階的に説明しようと思います。
ラムダ式への道 その1 delegate
delegate 日本語読みするときの一般名はデリゲートのようです。私は癖でどうしてもデレゲートって呼んでしまいます。
英和辞書的には委任・委譲となるのでしょうか。
C#では関数の型をdelegateとして宣言します、言い換えるとdelegateは型の一種ということになります、ちょっとくどいですね。
同様のものとして、クラスを宣言するときにはclass、構造体を宣言するときにはstruct
など何種類かありますね。
宣言の例を示します。
delegate string SomeFunction(int value);
引数がint value 返却型がstringのdelegateを宣言しました。引数にvalueと命名していますが実際にはvalueという名前はこの型の関数を使用するときには同じ名称である必要がありません。重要なのは引数と返却の型です。
ではこのデレゲートを変数として利用した例を示します。
class SomeClass { private SomeFunction _someFunction; public SomeClass(SomeFunction function) { _someFunction = function; } public string DoSomething(int value) { return _someFunction(value); } } class Program { static string DoSquare(int value) { return (value * value).ToString(); } static void Main(string[] args) { var some = new SomeClass(DoSquere); var value = 10; Console.WriteLine(some.DoSomething(value)); } }
クラスの動作を外部から制御することがこんな形で可能になります
このプログラムではコンソールに10の2乗で100が出力されることになります。しかしSomeClass自身には値を2乗するプログラムは実装されていません。
このようにをdelegateを使ってプログラムを適切に分解することで可用性が高まります(どう分解するかは腕の見せ所になるわけですが...)
今日はここまで
次回もひきつづきdelegateについて解説します。 ラムダ式にいつたどりつくのかは今のところ未定です。
お詫び
初出時のサンプルコードがコンパイルできないものでした、訂正させていただきました(お恥ずかしいかぎりです)。
LINQの話 その6
C#でのLINQの意味
C#のバージョン3.0でLINQとクエリ式(演算子)が追加されたことで、C#の進む方向に大きく与えたように思っております。
このとき同時に追加されたラムダ式や拡張メソッド、ひとつ前のバージョン2.0で追加された匿名メソッドやイテレーターも全てLINQを実現するために準備していたことなんだろうかと、今になって思いなおしています。
それ以前から仕事でC#を使っていましたがC# 3.0 と.NET Framework3.5がリリースされたとき、それぞれを別の事柄として理解はしていましたが未熟だったこともあり組み合わせて活用することができませんでした。
そのほんの少しあと、3.5SP1で.NET Frameworkライブラリに組み込まれたADO.NET Entity Frameworkの完成によってインピーダンスミスマッチングをなくそうという試みを実現し、言語とライブラリの完成度を飛躍的に高めたときだったんだなと思います。(でもこのバージョンのEntity Frameworkはどうしても使おうという気になれなかったんですが...、今のバージョンは良いです!、積極的に使いたいんですが社内のレガシー資産に阻まれて使う機会があまりないのが残念)。
この時の大きな言語拡張を経ることでC#が従来のオブジェクト指向言語でありながら関数型プログラミングを志向することもできるといった面白い特徴を備えることができ単価ないかと勝手に思っています。
一旦LINQの話はここまでにして、次回はC#のバージョンを少し進めて別の話をしてみようと思います。
LINQの話 その5
クエリ演算子の成り立ち
前回までで説明したLINQメソッドをより扱いやすくするためにC#を言語拡張してできた演算子です。
そもそもC#でクエリ式を実現するためにLINQメソッドやラムダ式などの拡張が行われたらしいです
詳しくはC#の歴史がマイクロソフトのドキュメントにあるので興味があればそちらもどうぞ
クエリ演算子とは
その演算子はSQLに習ってなんだろうけどfromやwhereそしてselect、もちろんorderbyも。order by とならないのはC#のスペースは単語を区切ってしまうので一つの演算子にするためにくっつけたんでしょうね。
ただそこでの演算はLINQメソッドが実行されるという演算です。
ここを理解しておくと何が行われているかすっと入ってくるんじゃないかな
クエリ演算子を使うとどうなるのか
まずはクエリ演算子を使わない場合
Person[] persons = new Person[] { new Person { Name = "Mike", Age = 55, Cars = new string[] { "Rover", "Vauxhall", "Morris" } }, new Person { Name = "Edo", Age = 48, Cars = new string[] {"Caterham", "Marcos" } }, new Person { Name = "Ant", Age = 40, Cars = new string[] {"Alpine", "Venturi"} }, }; IEnumerable<string> = persons.Where(p => p.Age >= 48) .OrderBy(p => p.Name) .Select(p => p.Name);
これをクエリ演算子を使ってクエリ式で記述するとこうなります。
//配列の宣言は省略... IEnumerable<string> = from p in persons where p.Age >= 48 orderby p.Name select p.Name;
かなりすっきりSQL文風に記述できました。
LINQメソッドを使う場合は使うごとにラムダ式を書くのがちょっとうっとうしかったりもするんですが、クエリ式の場合はfrom p in personsの部分でラムダ式の引数に相当する部分を一度書くだけです。あとはそれぞれの演算子の後にラムダ式の本体を書いてあげるだけです。
なぜ今更この説明を行おうと思ったのか
次回のネタにします
LINQの話 その4
並び替え
並び替えするのにArray.Sortを使ったり、List.Sortを使ったり、SortedListクラスを使ったりって.NET Frameworkにはいろいろありますが(知らないだけでもっとあるのかも)、それぞれ実装方法が違うし、一体どうやって使い分ければいいの?とか考えるのもちょっと面倒になってきます。
もちろん並べ替えロジックを自前で一から実装することもできるけど、そんなことは今更絶対やりたくない。
というわけで、LINQが一番柔軟で使いやすいなと思っています。
並び替えのサンプル
見てもらうのがほうか早いのでいきなりサンプルです。
Person[] persons = new Person[] { new Person { Name = "Mike", Age = 55 }, new Person { Name = "Edo", Age = 48 }, new Person { Name = "Ant", Age = 40 }, }; List<int> sorted = persons.OrderBy(p => p.Age).ToList();
これで年齢順にPersonオブジェクトを並べ替えしました。
さらに続けて別の要素で並べ替えしたい場合には続けてThenByメソッドを使います
List<int> sorted = persons.OrderBy(p => p.Age).ThenBy(p => p.Name).ToList();
補足すると、順序を逆にするOrderByDescending、ThenByDescendingもあるので、必要な場合はこっちも使えばよいです。
注意しておきたいこと
LINQで並び替えを行うとき、その内部処理ではIComparableインタフェースのCompareToメソッドを使っています、そのことでArrayクラスのSortメソッドなどと比較すると速度は数倍遅かったりします。
なので状況に応じて使い分けることが必要になってきます。
繰り返しになりますが、個人的にはソースコードの意味が分かりやすいのでよっぽどのことじゃない限りLINQで書いてしまいそうです。
次回
いままではLINQをメソッドとして利用する形で説明してきました。 次回は演算子としてのLINQを扱うクエリ式について書こうと思います。
いきものの進化
この夏アニマルプラネットの恐竜特集を延々と見続けて思った
なんか生物の進化の歴史が最近の研究で続々と過去の成果が書き換わっていっていますよね。
アニマルプラネットなんかでわかりやすく解説している番組を見ていると、自分が小学生・中学生のころかぶりついて読んでいた本や図鑑と全く違う世界を見せられて驚いたりします。
研究者の方々の努力というか執念みたいなものには本当に頭が下がります。
それでもその生物進化の歴史は、その時代の覇者である生物群からではなく、弱者から次代の覇者が生まれることを繰り返しているというところだけは、以前からの研究成果と変わっていないなあと理解しています。
このことが何を意味しているのかがとても気になります。
時代の覇者たちはその時代を生きるための適応が高度に進んでいてそれが覇者であることをさらに強化し、より覇権を握っていく。
一方、弱者は覇者が見向きもしないような様々な場で生きる術を身に着け何とか命をつないでいく。こんな感じなのでしょうか。
劇的な変化を迎えるとき、もしくはその予兆があるとき、時代の覇者が持っている適応能力で対応ができないようなことがあったとき、覇者たちは適応できず衰退し、弱者の中の変化する「場」に適応できる芽(目)をもったものが力をつけ、次代の覇者になっていくのでしょうね。
化石や地質の研究からこのようなことを読み取るんだから研究者の方々は本当にすごいなと思います。研究者の方々は知を持ち寄り、妥当と思える仮説や結論を見出していって、今後の社会に役立てようという高い意識をもって仕事しているんだろうな。
それで、この話は生物の話なのでEvolutionですが、企業や社会にも置き換えて考えることもできるよなっていいだけ大人になるとやっぱり考えちゃったりします。
企業ですとInnovationですよね、きっと。
あるInnovationが個人や企業によっておこると、それがある流れを作り社会全体の仕組みをも大きく変えてしまうようなInnovation変わっていくことは生きていると何度か経験してきているなと実感します。
そしてそれに適応できない人なり企業なりはその時代から淘汰されていってしまうのかと思うとなんかこわくなっても来ます。
でも何かしないとな、何も変わらない。
最近自分に言い聞かせています。