存在を忘れていたわけではない。
ただ単に書くネタがなかっただけ。
要するにWEBLOGってヤツに僕は向いていないのだと思う。
MIXIに書いた日記 → いい感じならBLOGへ、というスタイルが正解かな?
というわけでMIXIで書いた記事の再編集。
C# 3.5の仕様をいろいろごちゃごちゃ見てた。
なんだかPerlじみて来たな。とても自由に書けるけど・・・ゴニョゴニョ、ってやつ。
3項演算子とラムダ式の併用で、1行の処理量が異常になる。
例えば。ある自然数配列の要素を、偶数なら/2、奇数なら*2して総和を求めるということをすると。
c000: // C# 2.0 (without ternary operators):
c001: int[] nat2 = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
c002: int sum2 = 0;
c003: foreach (int x in nat2)
c004: {
c005: if (x % 2 == 0)
c006: sum2 += x / 2;
c007: else
c008: sum2 += x * 2;
c009: }
c010: Console.WriteLine(sum2);
c011:
c012: // C# 3.0 (with a lambda expression and a ternary operator):
c013: var nat3 = new[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
c014: var sum3 = nat3.Sum(x => x % 2 == 0 ? x / 2 : x * 2);
c015: Console.WriteLine(sum3);
// result:
r001: 70
r002: 70
あんまり実用性のある例とは言えないけど、まあ分かりやすくて且つ大幅な削減が見込める例ってことで・・・。
一応特徴的なところを列挙すると・・・
c013から
var nat3
C#3.0からローカル変数のvar宣言による型推論というのが効く。
何でも入る型、って言うとobjectのBoxingの事だから違う。
それにvarは型じゃない。あくまで「宣言」だから、コンパイル時にちゃんと適切な型として用意される。
同じくc013から
new[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
配列の初期設定時には、型名の省略が可能。初期化子から適切な型が推論される。推論の詳細は検証してないけど・・・。
そしてc014から
nat3.Sum(x => x % 2 == 0 ? x / 2 : x * 2)
C#3.5から実装のLINQによれば、列挙可能オブジェクト(Enumerable)にSQLライクな処理ができる。Sumもその一つ。
ちなみに、Sumの実装は
c015: // decimal IEnumerable<T>.Sum(Func<T, decimal> selector);
となっている。ただし、これにはdecimal、decimal?、int、int?・・・といったたいていの値型数値と、そのNullable型クラスのオーバーロードがついている。
とここで、.NET Framework 3.5からの独自delegateが。
c016: // Func<T, decimal>
これは、.NET Frameworkから加わったdelegate。
c017: delegate TResult Func<T, TResult>(T a);
c018: delegate TResult Func<T, TResult>(T1 a1, T2 a2);
など、引数の数4つ(T1,…T4)まで対応したdelegateがあらかじめ用意されている。
そして最後。ラムダ式。
こいつがなかなか癖強いんだけど。
今までは匿名のdelegateといやあ
c019: // delegate (int x)
c020: // {
c021: // return x * 2
c022: // }
みたいのを書いてたけど、これが
c023: // (int x) => { return x * 2; }
とゆーふーになる。=>はC#3.0から新しく用意された演算子。ラムダ表記のための演算子で、引数リストと式、あるいはステートメントを結びつける、きわめて左辺結合の強い演算子。
ちなみにc023は「ステートメント表記」のラムダ。このラムダを式で書くと
c024: // x => x * 2
となる。
このとき規則があって、
まずc023にある引数の(int x)のような型宣言は、型推論の対象となるため、一部例外はあるものの基本的に省略可能。だからたとえば
c025: Func
c026: f1 = (int x) => { return x * 2; };
というのは、左辺のFuncから引数のintとstringが型推論できるために、
c026': f1 = (x) => { return x * 2; };
と書ける。
加えて、ラムダ式の引数リストに引数が一つしかないときは、リストを囲む括弧がいらなくなる。つまりc026′はさらに省略できて、
c026'': f1 = x => { return x * 2; };
んで、ステートメント表記から式表記に直すことでreturnが省略できて
c27: Func<int, int> f2 = x => x * 2;
・・・ってえいうんだけどさ・・・
俺はc000~c010の例とc012~c015の例、どちらが素晴らしいか、って聞かれたら間違いなく上、と答える。
ラムダ式は個人的に気持ち悪すぎて、数値計算には使いたくないな。
出来るだけdelegateの代用、ってレベルに止めたいな、と思う。
varも使いたくないなあ。
後々変数の型を変えなきゃ行けないなんて、コアな型でも無い限り、それははっきり言って設計ミスだろう。
設計の段階で見込まれる拡張は、クラスの設計で対応するべきだと思う。
そんな考え方も古い、ってことなのかなぁ。
とにかく、安易にvarだのlambdaだの、ternaryだの使ったソースが蔓延しそうで怖いな、と思うのでした。