2010年08月03日

ForEach拡張メソッド

   このエントリーをはてなブックマークに追加 Clip to Evernote
元ネタ: 何周目?(R.Tanaka.Ichiro's Blog)
     http://blogs.wankuma.com/rti/archive/2010/07/27/191691.aspx

こんな拡張メソッド書けば、もっと楽になります。


呼び出し側のコードです。
標準のコードと上記拡張メソッドを使ったコードを比較してみました。


標準として用意されてほしかったですね。
こういったクラスを集めてライブラリにしても、
いちいち参照設定するのが面倒だし、コードをblogに公開するときにも使えないし... 不便です。
  

Posted by gushwell at 23:42Comments(2)TrackBack(0)

2010年07月21日

StringCollection を string[] に変換

   このエントリーをはてなブックマークに追加 Clip to Evernote

たぶん、ほとんど使われなくなったと思われる StringCollection クラスですが、 Visual Studio のプロジェクトの Properties フォルダにあるSettings.settings で設定した文字列リストは、StringCollection 型になっています。

これを stringの配列にしたいときのC#のコードです。


Enumerable.Cast メソッドで IEnumerableに型変換してあげれば、 LINQでおなじみの ToAray()メソッドで、string[] に変換できます。

C#2.0のときは、StringCollection.CopyToを使ったりしてましたが、Cast使うほうが直感的に 書けます。

  
Posted by gushwell at 22:32Comments(0)TrackBack(0)

2010年01月23日

再帰メソッドで、yield returnしたい

   このエントリーをはてなブックマークに追加 Clip to Evernote
再帰メソッドの戻り値を IEnumerable<T>にする場合についてまとめてみました。
順列を列挙するプログラムを例に説明します。
まず、以下のコードを見てください。


このコードの問題点は、_GetPermutations というメソッドの中で、Printメソッドを 呼び出している点です。
これだと、順列を求めるコードと、求めた順列を使って処理をするコードを明確に 分離することができません。
Permutationクラスは、いろんな場面で使える構造になっていないわけです。 問題が異なれば、Printメソッドの部分を書き直さなくてはなりません。

これを解決する方法のひとつが、イベントやdelegateを使う方法です。
順列が求まるたびに、イベントを発行すれば、Permutationクラスの利用者が Permutationクラスに手を入れることなく、自由に独自の処理を書くことができます。

ただ、この方法にも欠点があります。
例えば、GUIのあるプログラムで、次へボタンを押すたびに、順列をひとつずつ表示する ことを考えて見ましょう。こういった要求には、イベント発行する方法は上手く対応する ことができません。

Permutationクラスの利用する側で、Next メソッドなどを使い、ひとつずつ取り出せれば 良いですよね。
それには、Enumerate メソッドの戻り値を、IEnumerable<T>にするのが良さそうです。

以下に、戻り値を、IEnumerable<T> にしたPermutationクラスを示します。


注目すべき点は、再帰メソッドを呼び出して、返ってきた戻り値の扱いです(★印)。
return result とは書けません。
foreachで、一つ一つ要素を取り出し、yield return しています。こうすることで、再帰メソッドで、戻り値をIEnumerable<T>にすることができます。

全ての結果を Listなどに溜め込む方法でも、Enumerateメソッドの戻り値をIEnumerable<T>に出来ますが (この場合は、下請けメソッドの_GetPermutationsの戻り値は、IEnumerable<T>にはなりません)、 全てを列挙し終わらないと、Enumerateメソッドから制御を戻せませんので、 順列の元となる要素数が多い場合には、最初のひとつを取り出すのにも多くの時間を 要してしまい好ましくありません。

では、これを使う側のコードを示します。
まずはforeachで取り出すコード


次に、IEnumerator<T>.MoveNext, IEnumerator<T>.Currentを使うコード


ボタンをクリックするごとに、次の順列を取り出し、labelに表示しています。
  
Posted by gushwell at 23:27Comments(0)TrackBack(0)

2009年10月01日

拡張メソッド呼び出しの解決

   このエントリーをはてなブックマークに追加 Clip to Evernote
メソッドのオーバーロードの解決」の続きです。


というクラスがあったとします。
このCommandクラスに対し、以下のような拡張メソッドを定義しました。 拡張メソッドは、ジェネリックメソッドになっています。


この時


と呼び出したら、結果はどうなるのでしょうか。
結果は、


となり、拡張メソッドは呼ばれません. . .
拡張メソッドのほうが優先順位が高いはずはなく、この結果は、当然といえば当然ですね。

もし、拡張メソッドのほうを呼び出したければ、


のように書かないとだめみたいです。
ジェネリックな拡張メソッドを定義する際は、気をつけないといけませんね。
実は、これで嵌まりました。   
Posted by gushwell at 23:14Comments(0)TrackBack(0)

2009年07月08日

C#3.0のラムダ式とは

   このエントリーをはてなブックマークに追加 Clip to Evernote
2007年8月にアップした記事ですが、なぜか、Googleの検索にヒットしなくなってしまったみたいなので、再度掲載します。
言葉足らずのところがあるけど、なかなか簡潔にまとまっている記事だと思うのですが、検索に引っかからないのはもったいないです。

-----------------------------------------------------------
ラムダ式


このコードは、delegateの動きを説明した C#1.1のコード。

これが、C#2.0になると、こんな書き方になる。

わざわざ Tripleというメソッドを定義する必要がなくなったわけだ。

そして、C#3.0では、

ここまでは、C#2.0 と C#3.0 では、記述方法が変化しただけと見ることもできる。
さらに、(*)の行は、

と書ける。さらに、この(*)の記述が、

と短くすることができる。

いきなり、最後の文を見せられたときは、いきなり出てきた xって何だ?
と面食らったけど、順番に見ていけば、それほど難しくはない。
でも、慣れるのが大変そう。
  
Posted by gushwell at 22:26Comments(0)TrackBack(0)

2009年06月21日

カリー化について考えてみる

   このエントリーをはてなブックマークに追加 Clip to Evernote
もうひとつのブログ Gushwell's F# Programming Diary に「F#: カリー化と部分適用をC#で考えてみる(1)」を掲載しましたが、F#に興味のない方は、読んでいないと思うので、こちらのブログにもアップします。

多少、文言を変えていますが、内容は同じものです。

----------

関数 f(x,y) をカリー化するとは、関数 g を

が常に成り立つ関数にすることです。

関数gの引数は、x ひとつです。xは、元の関数 f の最初の引数ですね。
関数gの戻り値は、「元の関数 f の残りの引数を取り、fと同じ型の結果を返す関数」です。つまり、戻り値は関数です。
この、カリー化の定義を、もうすこし、わかりやすいC#風のコードを使って書きなおすと、

というコードで、常に、"a == b"が表示される 関数 g を定義することが、カリー化するということになります。
----------

一応、カリー化の定義が理解できたところで、具体的な例で考えてみます。


というメソッドを例に考えると、カリー化した関数は、

となります。
いきなり、結論から入ってしまいましたが、これを噛み砕いて説明しましょう。
CurriedSum関数は、x を引数として受け取ると、

という「デリゲート (ラムダ式)」を返しています。デリゲートというのは関数のことだと思ってください。
ラムダ式がよくわからないという方は、僕のBlogのここ「ラムダ式」を読んでみてください。

この「関数」を返すというのがこのCurriedSum関数の肝です。 なので、CurriedSum(10) と呼び出すと、

というデリゲート(int y を受け取り、10+y の結果を返す関数)が返ってきます。
なので、このカリー化した関数の使い方は


となります。引数が2つあったのが、一つずつ与える形に変更されたのがわかると思います。
つまり、

が成立したということです。
カリー化した関数は、こんな書き方もできます。

これで面白いのは、

などと書けることです。最初の 10 という引数をずーっと保持し続けることができます。
ということは、

のように、インクリメントする関数を定義することができます。
このように、カリー化した関数を呼び出し、あらたな関数を得ることを部分適用といいます。

関数型言語ではよく使われるみたいですが、普通のC#プログラマーは、こういったコードはまず書かないと思います。
なので、僕も、どう便利なのかがいまいち実感がありません。
  
Posted by gushwell at 21:56Comments(0)TrackBack(0)

2009年01月19日

えっ、ラムダ式 はデリゲート型ではないため、型 'object[]' に変換できません。?

   このエントリーをはてなブックマークに追加 Clip to Evernote
System.Windows.Threading.Dispatcherクラスの

public DispatcherOperation BeginInvoke (
DispatcherPriority priority,
Delegate method,
Object arg,
params Object[] args
)

の引数 argsにラムダ式を渡そうとしたら、

ラムダ式 はデリゲート型ではないため、型 'object[]' に変換できません。


というエラーメッセージが出てきた。
そうか、object型には変換できないのか、残念。

でも、コンパイラが出すこのメッセージはなんか変?
これをこの表現どおりに読めば、デリゲート型は、object[]に変換できるということになるのだが、もちろんそんなはずはない。

代入先が(型'object' であり)デリゲートで型ではないため、ラムダ式を 型 'object' に変換できません。

ということを言いたいのだと思うのだが...

http://msdn.microsoft.com/ja-jp/library/hy74she2.aspx
の日本語は、

デリゲート型ではないため、匿名メソッド ブロックを型 '型' に変換できません

この日本語も変だ。
この原文は、
Cannot convert anonymous method block to type 'type' because it is not a delegate type

itを中途半端に省略しちゃったから変な日本語になってしまったんですね。
  
Posted by gushwell at 21:58Comments(4)TrackBack(0)

2008年12月18日

C#で順列を列挙する

   このエントリーをはてなブックマークに追加 Clip to Evernote
1,2,3 という要素に対し、

{ 1,2,3 }
{ 1,3,2 }
{ 2,1,3 }
{ 2,3,1 }
{ 3,1,2 }
{ 3,2,3 }

とすべての順列を列挙するにはどうしたら良いのだろうか。

以下、試しに書いてみたコード。

static List<List<T>> AllPermutation<T>(T[] nums) {
    if (nums.Length == 1) {
        List<List<T>> list = new List<List<T>>();
        list.Add(new List<T> { nums[0] });
        return list;
    } else {
        List<T> subNum = nums.ToList();
        subNum.RemoveAt(0);
        List<List<T>> sub = AllPermutation<T>(subNum.ToArray());
        List<List<T>> result = new List<List<T>>();
        foreach (var x in sub) {
            for (int i = 0; i < x.Count() + 1; i++) {
                var temp = x.ToList();
                temp.Insert(i, nums[0]);
                result.Add(temp);
            }
        }
        return result;
    }
}

private static void Hoge() {
    int[] nums = { 1, 2, 3, 4, 5 };
    var r = AllPermutation(nums);
    foreach (var a in r) {
        foreach (var x in a)
            Console.Write(x);
        Console.WriteLine();
    }
}

たぶん、動いていると思う。
でも、数が多くなると、表示されるまで、間が開いてしまう。

やはりこういった時は、IEnumerableを戻り値にするほうがよさそうだ。
ついでに引数の型なども変更。

static IEnumerable<T[]> AllPermutation<T>(IEnumerable<T> nums) {
    if (nums.Count() == 1) {
        yield return new T[1] { nums.First() };
    } else {
        IEnumerable<T> subNum = nums.Skip(1); 
        var sub = AllPermutation<T>(subNum); 
        foreach (var x in sub) {
            for (int i = 0; i <= x.Count(); i++) {
                var temp = x.ToList();
                temp.Insert(i, nums.First());
                yield return temp.ToArray();
            }
        }
    }
}

このような再帰を使った場合、スタックオーバーフローが気になる。
でも、この手のコードを再帰を使わないで書く方法がわからない。
どうも、再帰なしで書くのが苦手だ。
Stackを使うことになるのだろうか?
  
Posted by gushwell at 21:52Comments(2)TrackBack(0)