デリゲートをむずかしくしているのはコールバックだね - C#

イベントを理解するにはデリゲートも理解してないと話が進まない。
とは言っても、デリゲートは理解に苦しむことが一つある。
何がデリゲートを難解にしているのか、それは正しく「コールバック」。
勉強のためと自身でデリゲートのコールバックを使ったコードを書いても、忘れた頃に読み返すと「何がしたかったんだろうか?」と解読に苦しんだりする。まだ私が未熟ということもあるが、可読性という側面から見て、このコールバックこそが他者に理解しづらくさせる要因では?と思ってしまう。

とりあえず基本から

delegate int del1(int x);

static void Main(string[] args)
{
    del1 lambda = x => x * x;
    del1 deldel = delegate(int x) { return x * x; };
    del1 delMethod = Method1;

    Console.WriteLine(lambda(5));
    Console.WriteLine(deldel(5));
    Console.WriteLine(delMethod(5));
}

static int Method1(int n)
{
    return n * n;
}
3種類あるが書き方が違うだけでどれも同じ処理。
これだけなら恐らく、プログラム始めて3ヶ月程度の方でも理解できるのでは?というぐらいに分かりやすい。

これにコールバックを組み合わせる。
要するにメソッドを引数にするデリゲートメソッドを作るということ。
delegate int del1(int x);
delegate int delCallback1(int x, del1 callback);

static void Main(string[] args)
{
    del1 lambda = x => x * x;
    del1 deldel = delegate(int x) { return x * x; };
    del1 delMethod = Method1;

    delCallback1 dc = Method2;

    Console.WriteLine(dc(5, lambda));
    Console.WriteLine(dc(5, deldel));
    Console.WriteLine(dc(5, delMethod));
}

static int Method1(int n)
{
    return n * n;
}

static int Method2(int i, del1 callback)
{
    return callback(i);
}

Method2のメソッドが引数として受け取ったメソッド(関数)を利用して値を返している。
ただこれだけでは芸がないのでもうひと工夫入れてみる。

まずはコードから
delegate int del2(int x, int y);
delegate int delCallback2(del2 callback);

static void Main(string[] args)
{
    delCallback2 dc2 = Method3;

    Console.WriteLine(dc2((x, y) => x + y));
    Console.WriteLine(dc2((x, y) => x - y));
    Console.WriteLine(dc2((x, y) => x * y));
    Console.WriteLine(dc2((x, y) => x / y));
}

static int Method3(del2 callback)
{
    return callback(Method4(),Method5());
}

static int Method4()
{
    return 5;
}

static int Method5()
{
    return 5;
}
処理方法は処理元から、値はMethod4とMethod5の作り次第で変わるというもの。
書いた本人、次読み返した時にすぐに理解できるかな…

LINQを触ってみる - C#

せっかく2008で書いてるんだからと「ちょっとLINQを試してみるか」的な感覚で。
とは言いつつも、デリゲートのお勉強 → ラムダ式 → LINQみたいな感じでたどり着いてしまったわけで、大した知識はもちろん... ない。

例えば、こんな配列があると想定して

int[] nums = { 5, 2, 3, 4, 5, 6, 11, 52, 7, 34, 87 };

Average
int配列の平均値を出す
Console.WriteLine( "配列の平均値 :{0}", nums.Average());

Sum
int配列の合計値を出す
Console.WriteLine("配列の合計値 :{0}", nums.Sum());

Where
条件に一致する値を返す
下の例では平均値以上の値を抽出。
var upper = nums.Where(num => num > nums.Average());
『upper』の型はIEnumerableになる。つまりint[]ではないということ。

Cast
キャストする
上記の例の変数 upper をint配列にキャストする。
Array.ForEach(upper.Cast<int>().ToArray(), i => Console.WriteLine(i));
int配列にムリクリキャストしてArray.ForEachを使用しています。
この場合「ToArray()」を忘れずに。

Reverse
逆順に並べ替える
IEnumerable<int> reverseNums = nums.Reverse();
numsの場合「87,34,7,52,...」となる。

Distinct
重複する値を削除
IEnumerable<int> DistinctNums = nums.Distinct();
numsの場合、5が一つ重複しているので後から出現する方の5が削除される。

合わせ技1
上記を組み合わせて使用する
var wdNums = nums
    .Where(num => num < nums.Average())
    .Distinct();
これは処理についてではなく、こういう書き方が分かりやすいらしい。

合わせ技2
SQLっぽい書き方で
var woNums =
    from n in nums
    where n < nums.Average() + 15 && n > nums.Average() - 15
    orderby n descending
    select n;
こういう書き方が望まれてる(?)、あるいは一般的なんでしょうか。
orderbyの昇順/降順の指定は「ascending」がdefaultなのでこちらを選ぶ場合は「orderby n」とし、ascendingの指定を省略可能。

イテレータ(Iterator)のサンプルを書いてみる [C#]

う~ん 確かにイテレータは覚えたら便利かも知れない。

と感じながらも何故にそこらで見るサンプルはイテレータのポイントである処理の中断(待ち)を入れないのだろう?
そんなプログラム走らせても単なる反復処理と変わらないじゃないか!
とか思いながら、イテレータのサンプル書いてみた。

自分でハードル上げてるわりには大したものではないが、所謂コルーチン的な動きになっていると思われ。

とりあえずコードから

static IEnumerable IteratorFunc(string s)
{
    for(int i = 0 ; i < s.Length; i++)
    {
        yield return s[i];
    }
}

static void Main(string[] args)
{
    foreach (char c in IteratorFunc("abc"))
    {
        Console.WriteLine("Key: " + c);
        while (c != Console.ReadKey().KeyChar) 
        {
            Console.WriteLine();
        }
        Console.WriteLine();
    }
}

『IteratorFunc(string)』は引数の文字列から一文字づつ返す。
foreach (char c in IteratorFunc("abc"))
foreach で IteratorFunc の戻り値が『c』に入り、
while (c != Console.ReadKey().KeyChar) 
コマンドプロンプトで押されたキーと比較し、同じキーが押されるまで while で回り、一致すると次の文字を表示していく、というもの。

指定の文字列から一文字づつ返す処理でわざわざイテレータを使っているわけですが、単なる反復処理では出来ない動作になっていると思います。


IEnumeratorを併用する

せっかくIteratorFuncメソッドの戻り値の型がIEnumerableなので、こんな書き方もできますよ、的な。
static void Main(string[] args)
{
    IEnumerator ie = IteratorFunc("abc").GetEnumerator();
    while (ie.MoveNext())
    {
        Console.WriteLine("Key: " + ie.Current);
        while ((char)ie.Current != Console.ReadKey().KeyChar)
        {
            Console.Write("\n");
        }
        Console.Write("\n");
    }
}
やってることは上と同じ。

簡単だし、覚えてしまえば実におもしろい。

インデクサのおはなし - C#

インデクサの説明に何故にHashtableが出てくるのか不明。
いろいろ試したが、結論はインデクサは「インデクサ」。
「そういうこともできるよ」的な話であって、ただ作りこめば可能なことを持ち出してインデクサについての説明をややこしくしないでほしいね。

基本的な形はこんな感じ。

class Indexer
{
    int[] _i = new int[10];

    public int this[int index]
    {
        get
        {
            return _i[index];
        }
        set
        {
            _i[index] = value;
        }
    }
}


内部で保持する型を指定しいるのがこの部分
int[] _i = new int[10];
この部分は現実的ではないが、あくまで「こんな形」程度に。


インデクサのポイント
public int this[int index]
この部分でthisはint、インデックスはintと指定している。


プロパティのアクセサ同様、このthisブロックを自分自身のアクセサとしている。
たったこれだけのこと。
上の例は結局のところ、int配列を包んでいるクラスでしかない。


実際に使用すると、このようにint型以外のインデックスの使用、または値を格納することは出来ない。
Indexer i = new Indexer();
i[1] = 5;
//i[3] = "A"; //エラー
//i["four"] = 4; //エラー


ちなみにもし、下記のようなことが出来るなら、Hashtable の話を持ち出すのも理解できるが、これは例外が発生する。
つまりできません。
//エラー
class Indexer
{
    public int this[string index]
    {
        get
        {
            return this[index];
        }
        set
        {
            this[index] = value;
        }
    }
}
内部で実際に値を保持する変数が無いので当然これでは×。



じゃあ、内部にHashTableを持てばどうなの。

そのまんまの形では全く無意味なのでkeyにstringを指定してみる。
class IndexerHashtable
{
    Hashtable _h = new Hashtable();
    public object this[string key]
    {
        get
        {
            return _h[key];
        }
        set
        {
            _h[key] = value;
        }
    }
}
これで、keyにstring型しか受け付けないインデクサの出来上がり。


keyだけではなく、格納する値の型も指定するならこんなのはどう?
class IndexerGenericList<T>
{
    Hashtable _h = new Hashtable();
    public T this[string strkey]
    {
        get
        {
            return (T)_h[strkey];
        }
        set
        {
            _h[strkey] = value;
        }
    }
}


あれ? 気付いてしまいました。


例えば、keyもvalueもstringを指定するならこんなのでいける。
Dictionary<string, string> dic = new Dictionary<string, string>();

Dictionaryクラス使えばいいんじゃないか...

てなワケで、こんな簡単なつくりなら実際必要ありませんでしたと。

イテレータ以前にIEnumerator [C#]

イテレータを調べていると、それ以前にIEnumeratorやIEnumerableをあまり理解してなかったので、また横道にそれて、まずこちらを調べることに。

まあ、簡単に解釈すると「要素を一つ一つ順番に取り出していくことが出来る」と言うことで、配列的なものを想像すればイイんじゃないかな。
アバウトすぎるとも思わないのだが。

msdnのヘルプでは

...コレクションを反復処理する列挙子を返します。
だそうで。


実際に書いてみる

単純な配列なら
int[] ia = new int[] { 1, 2, 3 };
IEnumerator iae = ia.GetEnumerator();
『IEnumerator』使うなら『using System.Collections;』が必要。


ジェネリックなListクラスならこんなのとか
List<int> i = new List<int>() { 1, 2, 3 };
IEnumerator<int> ie = i.GetEnumerator();


こんな感じでIEnumerator型にしちゃって以下のように使う。
using (IEnumerator<int> ie = i.GetEnumerator())
{
    while (ie.MoveNext())
    {
        Console.WriteLine(ie.Current);
    }
}
こんな感じで使えばヨロシ。

ちなみに最初の Current には何も入っていない。厳密には『未定義』。MoveNext() して最初の要素が返る。じゃないとWhile文の条件に入るといきなり MoveNext なんてつじつまが合いません。

上では using を使ってちゃんとDisposeするように書いたんですけど、try ... finally だとなぜかDisposeしてくれないんですけどナニユエ?
多分、using 使うこと自体無意味...


ついでにListBox.Itemsについて

ListBoxのItemsにはなんで GetRange メソッドがないんですかねぇ。
簡単に項目を取得する方法はないかちょっと調べてみた。

ListBoxのItemsをmsdnで見ると
public class ObjectCollection : IList, ICollection, IEnumerable
...
ListBox.ObjectCollection クラスは、ListBox に表示される項目を格納します。
はい、そうですか。つまりこういうことですか。
IList il = listBox1.Items;
IEnumerable iebl = listBox1.Items;
ICollection ic = listBox1.Items;

IEnumerableということは
IEnumerator ier = listBox1.Items.GetEnumerator();

string str = "";
while (ier.MoveNext())
{
    str += ier.Current + "\n";
}
return str;
こんなことができるわけですね。

速度求めるならこんなことしないでfor文で取得した方が速いんですけど、汎用性的な観点で必要な場面ってのが出てくるんでしょうかねぇ。

Mainはintを返すこともできるんだね

static void Main(string[] args)

この形が当たり前、と今更何も考えてなかったのですが、どうやらvoidだけじゃなく、『static int Main(string[] args)』 と、int型の戻り値を返すことが出来るらしい。

と、いつもながらいろいろ調べてる間に横道にそれて知ったことで、ワザワザ試したりはしない。
期待して来られたなら、スマンっ。

調べものをしていて横道にそれ、いったい何を調べていたのか忘れることも多々。

例えば今日はこんなものと遭遇。

別のEXEを起動して待ってみる
System.Diagnostics.Process.Start(@"c:\foo.txt");
これで関連付けられたプログラムが起動するらしい。試してないのであくまで「らしい」。
ポイントは『process.EnableRaisingEvents = true』ですかね。


Environmentクラス
ユーザードメインネームやらカレントディレクトリやらを読み書きできるようだが、カレントディレクトリは『System.IO』でも取得できる。何か違いがあるのか?

DOSコマンドを実行し出力データを取得する
引用:
WaitForExitはReadToEndの後である必要がある
(親プロセス、子プロセスでブロック防止のため)
とあるが『StandardOutput.ReadToEnd()』と『WaitForExit()』が何をするのか理解していれば必然的にその並びになるはず。


見返してみても何を調べていたんだか... 最近そんなんばっか。
でも、C#おもしろい。

数値の計算後の型は? 数値リテラルとかサフィックスとか

数値リテラル:

  • 整数 = int = 例:10
  • 小数点付き = double,float = 例:10.0

int型同士で計算すれば結果の値は常に『int』。
小数点以下切り捨て。四捨五入などしない。
int型の数値に「Math.Floor」などとは意味不明

double,floatやdecimalにキャストでもしないとコンパイルエラーになるはず。
某サイトでヘンなサンプルコードを見たのでちょっと確認も兼ねてです。

//リテラル
Console.WriteLine(
    "literal : {0}\n"+
    "literal(小数点付き) : {1}\n"+
    "literal(16進数) : {2}",
    10 / 3,
    10.0 / 3,
    0xa / 3);
結果:
literal : 3
literal(小数点付き) : 3.33333333333333
literal(16進数) : 3



//サフィックス
Console.WriteLine(
    "suffix[f] : {0}\n" +
    "suffix[f] : {1}\n" +
    "suffix[d] : {2}\n" +
    "suffix[m] : {3}",
    10f / 3, //f => float
    10 / 3f,
    10d / 3, //d => decimal
    10m / 3); //m => double

結果:
suffix[f]: 3.333333
suffix[f] : 3.333333
suffix[d] : 3.33333333333333
suffix[m] : 3.3333333333333333333333333333



変数
int i = 10;
double a = 10;
Console.WriteLine(
    "int : {0}\n"+
    "double : {1}\n" + 
    "\u0061 : {2}",
    i / 3,
    a / 3,
    \u0061 / 3);//\u0061 => a

結果:
int : 3
double : 3.33333333333333
a : 3.33333333333333



16進数の『0xa』は10進数の『10』。
『\u0061』は変数『a』のことで、上記の場合『変数 double a』を参照していて、ちゃんと計算できるのですが、実際にこんな書き方してる人がいたらちょっとひく。

余談ですが、Uint16のMaxValueを見るとドラクエⅠの経験値を思い出す。

Replace code-string β

コードをブログに載せるときに特定の文字を書き直すのが面倒なので、変換するものを作成しました。

Replace code-string



出来ることは二つです。

まず、対象となる文字をページ中央のテキストエリアに貼り付けます。

ページ左側のチェックボックスで変換したい項目を選択し、[実行]ボタンを押すと文字が変換され、ページ左側のテキストエリアに表示されます。

ベタ貼りしたいコードをタグで囲むことを想定し、いくつかのタグから一つ選び、[実行]ボタンを押すとテキストエリアに貼り付けた文字が指定したタグで囲まれ、ページ左側のテキストエリアに表示されます。

よろしければ使ってやって下さい。



▽免責事項▽
このWebページを使用、または使用できなかった際のいかなる損害も、
制作者 Y@$ 及び humming bird は、その責を負いません。
ご自身の責任においてご使用ください。

JavaScript - 正規表現 パターン・チェック β

JavaScriptの正規表現パターンをチェックするWebページを作成しました。
matchとreplaceに対応。簡単なパターンの作成、確認に使えたら良いかなと。
サンプル・パターンはぼちぼち追加していこうかと考えています。

JavaScriptの正規表現 パターン・チェック

http://humming.bird.2k8.googlepages.com/javascript_regexp_pattern_check.html

動作確認:
  • OS:Vista
  • ブラウザ:Firefox,Chrome,IE7
ご利用は自己責任でお願い致します。
不具合等ありましたらご報告頂けると幸いです。

追記:

2009/03/05
jQueryでいじりつつ、サンプルパターンを追加。

『/』の件について:
この件に関する記載を追加。

2009/03/08
サンプルパターンの見直し。
サンプルパターンの表題の見直しに伴うサンプルの配置換え。

2009/07/02
リニューアルしました!!
このページを置いているGPCがそろそろサービス停止になりそうなので、こちらに移動しました。

http://humming-bird.appspot.com/tool/js.regexp.pattern.check.html

ついでに色々と見直して、デザインも一新しましたので、よろしくお願いします。

Picasa ウェブアルバムが秘かにチョロメに対応

知らぬ間にPicasa ウェブ アルバムがChromeに対応していたことに気付いたのでご報告。

Chromeは初公開時点ではPicasa ウェブ アルバムにある画像(アルバム)をダウンロードすることは出来ませんでしたが、ごく最近の変更と思われますが、ダウンロードできるようになりました。

初公開時には「自サービスの機能を一部利用できないブラウザを開発しました」というかなり呆れたシロモノだったが、これでようやくまともになったかな、と思われる。

試しにSafariでもダウンロードできるか確認したところ、やはりできました。

これって棚ボタ的にPicasa ウェブ アルバムの対応ブラウザが増えたってこと?
逆にSafariに対応したらChromeにも対応できたとか?

まあ、どっちでもイイんですけど...

余談ですが、Picasa3のbeta版が出てるみたいですね。
ちなみに『Picasa』はPCにインストールして使うアプリケーションで、『Picasa ウェブ アルバム』はWebサービスです。

インターフェイスを理解する - C#

『隠蔽化』ってどういうこと?

まず、インターフェイスを調べるとよく目にするのは『隠ぺい化』という言葉。
これはあるクラスの中に幾つものメソッドやプロパティが詰め込まれていたとします。しかし、そのクラスのインスタンスの生成時に型として指定したインターフェイスによって、使用できるもの(プロパティやメソッドなど)が限定されます。そのクラスの中で使用できるものを限定できるということです。
例えば該当クラスの基底クラスとして、getアクセサしか持たない読み取り専用のインターフェイスを作成し、このインターフェイスを継承します。不用意にプロパティを書き換えられないようにする、という使い方が可能と言えます。

継承されることが前提

インターフェイスは継承されることが前提です。どんなメソッドやプロパティが必要か、派生クラスの設計図と捉えることができます。

中身はなし

あくまで設計図ですので、アクセサやメソッドの中身(実装)は派生クラスで行います。プロパティの型の他、メソッドの引数や戻り値の型などを指定します。

インターフェイスは型

該当クラスのインスタンスを受け取る際の引数の型として、その状況に最適なインターフェイスを指定する、という方法で間違ったクラスの使い方を防ぐ予防策となるのではないでしょうか。渡された側で型(インターフェイス)をキャストでもしない限り、つまり渡された型のままで使用すれば間違いを減らすことが出来るということです。

インスタンスは生成できない

インターフェイスはインスタンスを生成できません。

インターフェイスを継承する派生クラスはPublicなどのアクセス可能な修飾子です。ということはインターフェイスを介さずともそのクラスを使用することは可能ということになります。
インスタンスを生成する際の型の指定としてインターフェイスは使われてもインスタンスは該当のクラスということになります。ということは強制的にインターフェイスを使用させることはできないということでもあります。そしてインターフェイスは所詮「型」であるため、キャストしてしまえばインターフェイスで定義したルールを無視することも出来てしまいます。

このため結局のところ、コーディングの際の規則として、例えば「このクラスをここで使用する場合はこのインターフェイスを使用して下さい」的なルールが存在すること前提の機能と考えた方が良い。

その他

インターフェイスで定義されたプロパティやメソッドなど、これら全てが派生クラスに存在しなければならない。
逆に継承するインターフェイスに定義されていないプロパティやメソッドなどを派生クラスで作成することは可能です。

インターフェイスから継承したメンバは基本的には全てPublicです。
クラスは一つしか継承出来ません(多重継承禁止)がインターフェイスはいくつでも継承可能です。
インターフェイスでコンストラクタの定義はできません。
delegateでイベントに使用するなどのメソッドの型を定義することは出来ません。
operatorをイジることは出来ません。

Sony Style(ソニースタイル)
デル株式会社

Recent Posts