Hatena::ブログ(Diary)

当面C#と.NETな記録 このページをアンテナに追加 RSSフィード

2007/7/29 (日)

[] C#3.0仕様チェック  C#3.0仕様チェックを含むブックマーク  C#3.0仕様チェックのブックマークコメント

8/8までC#3.0までの統合仕様書をレビューして、blogにコメントくださいとのこと。

http://blogs.msdn.com/charlie/archive/2007/07/26/csharp-language-specification-version-3-0-available-for-review.aspx

ワード文書の仕様書はこちら。VS2008β2のお供に。

(追記)Finalizerという呼び方が消えて、Destructorが復活した模様。なんでだ…

トラックバック - http://d.hatena.ne.jp/siokoshou/20070729

2007/7/27 (金)

トラックバック - http://d.hatena.ne.jp/siokoshou/20070727

2007/7/26 (木)

[] LINQ to Objects で グループ化  LINQ to Objects で グループ化を含むブックマーク  LINQ to Objects で グループ化のブックマークコメント

SQL を勉強しようと↓の本を読んでいます。SQL ってうまいの?ってレベルの私にもわかりやすく、薄いのに丁寧でよい本です。順に読めば SQL がしっかり理解できます。個々の DB について詳しい本ではなく、SQL 特にクエリーについての「言語の入門書」といった感じ。MySQL で解説していますが、一部 MySQL にないものでも記述があったり、Oracle ではこう、SQL Server ではこうと記述も(ちょっと)あります。各 DB の詳しい本とあわせて用意するとよいかと思います。唯一の欠点は表紙がきもいこと!常にひっくり返して置くこと!

初めてのSQL

初めてのSQL

今、グループ化のあたりを読んでいるので、LINQ to Objects ではどう書くのか試してみました。


コード

データはこないだ使った Person クラスのやつ。ちなみに、Person クラスの「public string Name { get; set; }」を「public string Name { get; private set; }」にしたら、エラーでした。「new Person { Name = "Wataru Abe", Gender = Gender.Male, IsActive = true },」形式の初期化を使ってるため、set_Name などを暗黙のうちに使ってしまってたのが原因です。そういう形式のコンストラクタを暗黙に作って呼べばいいのに!β2では匿名型が不変になるので、もしかしてそうなるんでしょうか。

まずはコードと実行結果から。Orcasβ1で試しました。

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
  static void Main()
  {
    var persons = new[] {
      new Person { Name = "Wataru Abe", Gender = Gender.Male, IsActive = true },
      new Person { Name = "Masao Sueda", Gender = Gender.Male, IsActive = true },
      new Person { Name = "Sae Nakarai", Gender = Gender.Female, IsActive = true },
      new Person { Name = "Shiori Yamamoto", Gender = Gender.Female, IsActive = true },
      new Person { Name = "Satoshi Hatakeyama", Gender = Gender.Male, IsActive = false },
    };

    // (1)
    var list = from p in persons
               group p.Name by p.Gender;
    foreach ( var x in list )
    {
      Console.Write( x.Key + ": " );
      foreach ( var y in x )
      {
        Console.Write( y + ", " );
      }
      Console.WriteLine();
    }
    Console.WriteLine();

    // (2)
    var keyNum = persons.GroupBy( p => p.Gender ).Count();
    Console.WriteLine( keyNum );
    Console.WriteLine();

    // (3)
    // SELECT gender, COUNT(*) num
    // FROM persons
    // GROUP BY gender;
    var num = persons.GroupBy( p => p.Gender )
      .Select( g => new { Gender = g.Key, Num = g.Count() } );
    foreach ( var x in num )
      Console.WriteLine( x );
    Console.WriteLine();

    // (4)
    // SELECT gender, COUNT(*) num
    // FROM persons
    // GROUP BY gender
    // HAVING COUNT(*) > 2;
    var num2 = persons.GroupBy( p => p.Gender )
      .Select( g => new { Gender = g.Key, Num = g.Count() } )
      .Where( t => 2 < t.Num );
    foreach ( var x in num2 )
      Console.WriteLine( x );
    Console.WriteLine();

    Console.ReadKey();
  }
}

public enum Gender { Male, Female }

sealed class Person
{
  public string Name { get; set; }
  public bool IsActive { get; set; }
  public Gender Gender { get; set; }
}

実行結果。

Male: Wataru Abe, Masao Sueda, Satoshi Hatakeyama,
Female: Sae Nakarai, Shiori Yamamoto,

2

{ Gender = Male, Num = 3 }
{ Gender = Female, Num = 2 }

{ Gender = Male, Num = 3 }

クエリが4つあります。順に見ていきます。


グループ化とは?

まずはグループ化とは何か。一つ目の例を見ればわかると思います。ここではSQL 風の「クエリ式」で書いてみました。

var list = from p in persons
           group p.Name by p.Gender;

foreach ( var x in list )
{
  Console.Write( x.Key + ": " );
  foreach ( var y in x )
  {
    Console.Write( y + ", " );
  }
  Console.WriteLine();
}

persons を Gender の値によって2つのグループに分けました。最終結果には名前だけを取り出しました。「group p.Name by p.Gender」は p.Gender の値ごとにグループ化し、p.Name を取り出しています。

Male: Wataru Abe, Masao Sueda, Satoshi Hatakeyama,
Female: Sae Nakarai, Shiori Yamamoto,

var listと書きましたが、正確には IEnumerable< IGrouping< Gender, string > > です。IGrouping は Key プロパティと GetEnumerator() メソッドを持ち、グループ一つを表します。グループのシーケンスが結果となっているわけです。

この例では Key = Gender.Male のグループと、Key = Gender.Female のグループがあり、外側の foreach でこれを取り出します。内側の foreach では string の Name を取り出しています。

IEnumerable<T> は LINQ では主に「シーケンス」と呼ばれますが、これを「データ構造」と考えるとすんなり理解できます。「遅延リスト」と考えてください。そう思えないうちは、var を使って LINQ でいろいろ遊んでいればそのうちなじむと思います。

var を使わずに書けばこうなります。見ただけでひるんでしまいます。

IEnumerable<IGrouping<Gender, string>> list2 = from p in persons
    group p.Name by p.Gender;
foreach ( IGrouping<Gender, string> x in list2 )
{
  Console.Write( x.Key + ": " );
  foreach ( string y in x )
  {
    Console.Write( y + ", " );
  }
  Console.WriteLine();
}
Console.WriteLine();

グループ化と集約演算

グループ化した結果に対して、集約関数を使うのはよくある操作です。集約演算は Count, LongCount, Sum, Min, Max, Average そして何でも屋の Aggregate。

男性は何人いて、女性は何人いるか調べてみます。グループ化と Count を使えばよさそうです。今度はドット表記で書いてみます。

var keyNum = persons.GroupBy( p => p.Gender ).Count();

こうすると結果は 2 とだけ返ってきてうまくいきません。これは、データには Gender が2種類あったことを調べてしまっています。

正しいクエリをSQLで書いてみます。

SELECT gender, COUNT(*) num
FROM persons
GROUP BY gender;

結果には 2列必要で、Gender と その数 です。これを LINQ にしてみます。

var num = persons.GroupBy( p => p.Gender )
  .Select( g => new { Gender = g.Key, Num = g.Count() } );

GroupBy の結果は、グループのシーケンスなので、グループの Key と グループ内のデータ数を数えたもの を最終結果にしました。今度は期待通りの結果が返ってきました。

{ Gender = Male, Num = 3 }
{ Gender = Female, Num = 2 }

HAVING は LINQ to Objects でどう書く?

次はグループにフィルタ条件をかけてみます。前の例で、数が 2 より大きいグループだけを表示したいとします。つまり、Male だけが表示されるようにしてみます。

SQL でグループにフィルタ条件を指定するには where ではなく having を使います。これは、SQL では where 節が group 節より先に評価されるため、where を評価する時点ではまだグループがないためです。薄っぺらな本なのにこんなところまで丁寧に説明してあります。

SELECT gender, COUNT(*) num
FROM persons
GROUP BY gender
HAVING COUNT(*) > 2;

しかし、LINQ では評価順序をユーザが決めれるので、where で書けてしまったりします。

var num2 = persons.GroupBy( p => p.Gender )
  .Select( g => new { Gender = g.Key, Num = g.Count() } )
  .Where( t => 2 < t.Num );
{ Gender = Male, Num = 3 }

何それ!って感じですが、SQL と LINQ to Objects の違いとして受け入れるのがよいと思います。

select と where を逆に書くこともできますね。

var num3 = persons.GroupBy( p => p.Gender )
  .Where( t => 2 < t.Count() )
  .Select( g => new { Gender = g.Key, Num = g.Count() } );


練習: すべての例を、クエリ式とドット表記をここを参考にしながら、もう一方の表記に書き換えてみてください。ちょっと難しいパズルです。

GroupBy( F, G ) が group G by F になることに注意してください。また、group by の後には into を使ってクエリーを継続します。

参考: グループ化や集約関数の説明

トラックバック - http://d.hatena.ne.jp/siokoshou/20070726

2007/7/23 (月)

[] unfold に挑戦  unfold に挑戦を含むブックマーク  unfold に挑戦のブックマークコメント

id:NyaRuRu:20070722#p2 のお題がおもしろいので、unfold 書いてみました。Orcas 立ち上げるのが面倒だったので C#2.0 で。

コードは最後にして、その前にいろいろと。


unfold ???

まずは unfold が何なのかわからなかったので Haskell の定義から。Hugs の List.hs から引用。

名前からして fold の反対だろうとは思うけど、畳み込みの反対って余計想像できなかったりw

unfoldr      :: (b -> Maybe (a, b)) -> b -> [a]
unfoldr f b  =
  case f b of
   Just (a,new_b) -> a : unfoldr f new_b
   Nothing        -> []

Haskell には unfoldr がありました。foldr、foldl はありますが、unfoldl はなさそう。

関数を次々と評価してリストを生成する機能だそうです。unfoldr の一つ目の引数「(b -> Maybe (a, b))」は「bを引数とし、戻り値 Maybe (a, b) を返す 関数」です。二つ目の引数(に見えるもの)は 種となる b。戻り値は、リスト [a] 。a と b はテンプレートの型パラメータにあたります。Maybe は C# の Nullable みたいなもの。でも、ずっと強力。詳しくは「ふつうのHaskellプログラミング」の266ページを。(a, b)はタプルでC++のpair。

NyaRuRuさんの記事にある、F# の unfold を使ったフィボナッチを Haskell にしてみます。F# のコードははじめて見たけど、たぶんこういうことだと思う。

fib = unfoldr ( \( x, y ) -> Just ( x, ( y, x + y ) ) ) ( 1, 1 )

実行は take 20 fib で。結果はあってるので、よさげ。久しぶりの Haskell ですっかりいろいろ忘れてて大変でした…。

で、unfold の肝は「いかに終了をきれいに扱うか」だと思うので、フィボナッチだと無限に続いて終わらないので別の終わる例を。


tails

Haskell本をパラパラ開いて、tails 関数がよさげだったので例に取り上げます。まずは GHCi での実行例から。

*Main> tails "hoge"
["hoge","oge","ge","e",""]
*Main> tails [1,2,3]
[[1,2,3],[2,3],[3],[]]

与えられたリストから一つずつ要素を削っていき、そのリストを返します。なんでこんなものが標準ライブラリにあるのかさっぱりですが、急須を一命令で描けたりする世界もあることだし気にしない方向で。

これを unfold を使って書いてみます。

sioTails = unfoldr pat
  where
    pat [] = Nothing
    pat (x:xs) = Just ( x:xs, xs )

実行結果はこう。

*Main> sioTails "hoge"
["hoge","oge","ge","e"]
*Main> sioTails [1,2,3]
[[1,2,3],[2,3],[3]]

本物と比べると最後の [] が抜けてます。でも、unfold を試すだけなら困らないのでこれを C# でやってみます。


いよいよ C#

using System;
using System.Collections.Generic;

namespace unfold
{
  using PairString = KeyValuePair<string, string>;
  using NullablePairString = Nullable<KeyValuePair<string, string>>;
  using PairSeq = KeyValuePair<IEnumerable<int>, IEnumerable<int>>;
  using NullablePairSeq = Nullable<KeyValuePair<IEnumerable<int>, IEnumerable<int>>>;
  using PairInt = KeyValuePair<int, int>;
  using PairPairInt = KeyValuePair<int, KeyValuePair<int, int>>;
  using Result = Nullable<KeyValuePair<int, KeyValuePair<int, int>>>;

  delegate R Func<A, R>( A a );

  class Program
  {
    static void Main()
    {
      // sioTails "hoge"
      IEnumerable<string> tails = Unfoldr<string, string>(
        delegate( string s )
        {
          if ( string.IsNullOrEmpty( s ) ) return null;
          return new NullablePairString( new PairString( s, s.Substring( 1 ) ) );
        },
        "hoge" );

      foreach ( string s in tails )
        Console.Write( s + ", " );
      Console.WriteLine();

      // sioTails [1,2,3]
      IEnumerable<IEnumerable<int>> tails2 = Unfoldr<IEnumerable<int>, IEnumerable<int>>(
        delegate( IEnumerable<int> seq )
        {
          using ( IEnumerator<int> it = seq.GetEnumerator() )
          {
            if ( !it.MoveNext() ) return null;
          }
          return new NullablePairSeq( new PairSeq( seq, Skip( seq, 1 ) ) );
        },
        new int[] { 1, 2, 3 } );

      foreach ( IEnumerable<int> seq in tails2 )
      {
        Console.Write( "[" );
        foreach ( int n in seq )
          Console.Write( n + ", " );
        Console.Write( "], " );
      }
      Console.WriteLine();

      // fib = unfoldr ( \( x, y ) -> Just ( x, ( y, x + y ) ) ) ( 1, 1 )
      // take 20 fib
      IEnumerable<int> fib = Unfoldr<PairInt, int>(
        delegate( PairInt pair )
        {
          return new Result(
            new PairPairInt( pair.Key,
              new PairInt( pair.Value, pair.Key + pair.Value ) ) );
        },
        new PairInt( 1, 1 ) );

      int count = 20;
      foreach ( int n in fib )
      {
        if ( count <= 0 ) break;

        count--;
        Console.Write( n + ", " );
      }
      Console.WriteLine();
      Console.ReadKey();
    }

    static IEnumerable<R> Unfoldr<A, R>( Func<A, Nullable<KeyValuePair<R, A>>> f, A a )
    {
      while ( true )
      {
        Nullable<KeyValuePair<R, A>> pair = f( a );

        if ( !pair.HasValue ) yield break;

        yield return pair.Value.Key;
        a = pair.Value.Value;
      }
    }
  }
}

う〜、やっぱりC#3.0で書けばよかった。2.0だと汚くなりすぎ…。

Skip関数は割愛。LINQ相当のものをご想像願います。

実行結果。

hoge, oge, ge, e,
[1, 2, 3, ], [2, 3, ], [3, ],
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181,
 6765,

Unfoldr に渡す関数では戻り値に Nullable を被せて、Unfoldr でそれを剥ぐという、いまいちな作り。

Maybe モナド構造体を書いてみようと挑戦したけど、やっぱり無理でした。「(Just x) >>= f = f x」と「Nothing >>= f = Nothing」をなんとか書けないものかな。それとも全然別のうまい手がないかなぁ。

[] IronRubyプレアルファ  IronRubyプレアルファを含むブックマーク  IronRubyプレアルファのブックマークコメント

出たそうです。

http://www.iunknown.com/2007/07/a-first-look-at.html

via 荒井さんのBlog

http://blogs.msdn.com/shozoa/archive/2007/07/23/dlr-ironruby.aspx


(追記)Astクラスがおもしろいことになっていますが、昔どこかで見た partial を使ったビジターパターンなんだと思います、たぶん。partial を使ってインタプリターパターンの各ノードクラスに追記するような感じで Ast クラスが細切れに含まれています。でも、ぐぐっても partial を使ったビジターパターンのオリジナル記事が見つからない…。おもしろかったので記事にしたような気がしてたけど、書いてないですね…。

(追記2:20070725)おもしろいものを見つけたらその場でメモっとかないから partial を使ったビジターパターンのURL忘れるんだと反省。なのでメモしまくる。

partial を使った Visitor パターン (以前見つけたのとは別の人によるものかもしれないけど、とりあえず)

http://www.removingalldoubt.com/PermaLink.aspx/bece3be7-04a1-4130-b1ac-0b88c94e7708

ScuttGuさんによる IronRuby で WinForm と WPF のサンプル

http://weblogs.asp.net/scottgu/archive/2007/07/23/first-look-at-ironruby.aspx

(コメント欄に今週遅くにOrcasβ2出るよーとか書いてあったり)

John Lamさんの投稿をよく読むと id:siokoshou:20070514 で紹介した ILの魔術師(?) Haibo Luo さんは IronPython チームではなく、IronRuby チームと記述あり。まあ、DLR の IL 生成あたりをやっているんだろうな。

IronRuby に関しては、System.Int32 での計算しかできなかったり、偉大な p がなかったり、なにかと例外ばかり起きます。プレアルファなのでまぁ。

NyaRuRuNyaRuRu 2007/07/24 01:27 >unfold
Orcas で試すとおもしろいですよ.
C# 3.0 コンパイラの型推論の癖なのかもしれませんが,T? が絡んだときの型推論がどうも思うようにうまくいってくれなくて,T に匿名型を使いつつ,きれいな unfold を書くのは結構大変です.
いくつか無理矢理推論させるトリックは思いついたのですが,いっそのこと停止条件は別の匿名メソッドで与えた方が良いのかもしれません.

siokoshousiokoshou 2007/07/25 00:36 お〜、ちょっとやってみます。Scheme の unfold は停止条件を別に与えているようですね。

2007/7/21 (土)

[] LINQSQLの検索CASE式  LINQでSQLの検索CASE式を含むブックマーク  LINQでSQLの検索CASE式のブックマークコメント

LINQのおかげでSQLがすっかり関数型言語高階関数群にしか見えなくなってしまいました。でもJoinのようなRDBらしい機能はやっぱりよくわからないので、SQLの入門書を借りてきて読んでます。

その中でSQLにもCASE式があることを知ったので、拡張メソッドでCASE式を作り、FizzBuzzを書き直してみました。id:siokoshou:20070712のNyaRuRuさんのコメントのラムダ式でif elseを並べるのに比べて、遅くなるだけで利点は何もないような気がしますが、まぁ書いてみました。クロージャで制御文を実現してみる例と考えるとちょっとはおもしろみがあるのかも?ちなみに、Smalltalkの制御文はクロージャを引数にとる形で実現してるようです。

SQLの検索CASE式の見た目はこう。

CASE
  WHEN condition1 THEN result1
  WHEN condition2 THEN result2
  ...
  ELSE resultN
END

完全に真似するのはいろいろ面倒なので、もどきで済ませます(^^;

Whenの部分を、条件式と結果の式を持つクラスにします。Caseに渡す最後のWhenはelseに対応するために、条件式は必ずtrueを返す式とする制限付きです。手抜きです。

Orcasβ1で試しました。最新のCTPはまだ落としてないんで。

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
  static void Main()
  {
    Enumerable.Range( 1, 100 ).Select(
      m => m.Case(
        new When<int, string>( n => 0 == n % 15, n => "FizzBuzz" ),
        new When<int, string>( n => 0 == n % 5,  n => "Buzz" ),
        new When<int, string>( n => 0 == n % 3,  n => "Fizz" ),
        new When<int, string>( n => true, n => n.ToString() )  // else
      ) ).ToList().ForEach( Console.WriteLine );
    Console.ReadKey();
  }
}

public static class Ext
{
  public static TResult Case<TSource, TResult>(
      this TSource target, params When<TSource, TResult>[] when )
  {
    for ( int i = 0; i < when.Length; i++ )
      if ( when[ i ].Condition( target ) )
        return when[ i ].Result( target );
      return default( TResult );
  }
}

public sealed class When<TSource, TResult>
{
  public Func<TSource, bool> Condition { get; private set; }
  public Func<TSource, TResult> Result { get; private set; }

  public When( Func<TSource, bool> cond, Func<TSource, TResult> r )
  {
    this.Condition = cond;
    this.Result = r;
  }
}

SQLのCASE式というよりは、Schemeのcondですね。

Shiraleeさんに教えてもらったprivateを使ってみました(ありがとうございます)。

「new When<int, string>( n => 0 == n % 15, n => "FizzBuzz" ),」の部分、なぜか型推論がうまく働かなかったので、<int, string>なんて付けています。なんでだろ。

結論。LINQ to ObjectではCaseは無駄に遅いだけで、いらないですね。LINQで遊ぶときは、ついC#の構文を使ったら負けと思ってしまうんだけどw、SQLとC#を混ぜて使える点は大きなメリットなのでどんどん混ぜるべきですね。

ところでLINQ to SQLにCaseはあるんでしょうか?

(追記)このCaseってすべての型を拡張してしまいますね。最悪な例だな…。

トラックバック - http://d.hatena.ne.jp/siokoshou/20070721

2007/7/19 (木)

[] LINQ日本語ドキュメント  LINQ日本語ドキュメントを含むブックマーク  LINQ日本語ドキュメントのブックマークコメント

via ひろえむさんのところ http://blogs.wankuma.com/hirom/archive/2007/07/18/85770.aspx

既に一部古いところがあるっぽいけど、日本語&一覧性が高いのがありがたい。わざわざVPC立ち上げてドキュメント読むのだるいし。

LINQは中毒性が高くてついつい遊びすぎてしまうのでご注意をw

[] 分解してしまう  分解してしまうを含むブックマーク  分解してしまうのブックマークコメント

関数型言語は、何もかも徹底的に分解してその意味を熟考し、その結果汎用にできるものを汎用的に拡張し、全体を再構築した、そんな言語だと思います(特にLisp)。それで高階関数やら、奇妙な概念の継続みたいなものが生まれてきたんだろうなぁと想像します。

匿名関数を例にこれを考えて見ます。SICPからの受け売りですがw

int add( int x, int y )
{
  return x + y;
}

こういう関数を作ることを、「関数の本体を作る」ことと、それに「名前を付ける」ことの2つに分解して考えた結果、「名前を付ける」部分を省けば、それが「匿名関数」です。

( x, y ) => x + y

SICP、刺激的ですよ。

p-nixp-nix 2007/07/22 01:48 短いながらも秀逸です!そういうことか、とシナプス繋がりました。
SICP読む手間が省けました。オイ。読みますです。積ん読してるので。

NyaRuRuNyaRuRu 2007/07/22 02:32 http://arton.no-ip.info/diary/20070605.html#p02
を読んで,「匿名」と「無名」の使い分けはちょっと注意するようにしています.
( x, y ) => x + y
はイメージ的には「無名」かも.まあ定義というよりはニュアンスで言ってますけど.

siokoshousiokoshou 2007/07/22 04:21 > SICP読む手間が省けました。
いやいや、読んで理解してそれを活かしてこそ意味があるので、やっぱり読みはじめないと!とか言って私も積読なんですがww ときどき思い出したようにひっぱりだしてちょっと読んで、わかんねorzってなりますw

> 「匿名」と「無名」の使い分け
うーん…、無名と言われればそういう気もします。でも使い分けはこだわらなくてもいいかなと私は思ってしまったり。発音時の音は前にも書きましたが、そのほかに「無名」ってちょっと寂しげな悲しげな、そんなイメージがただよってしまうのであまり好きじゃないんですよね。

トラックバック - http://d.hatena.ne.jp/siokoshou/20070719

2007/7/17 (火)

[] Google Book検索のブラウザ用検索プロバイダ (IE7/Firefox2)  Google Book検索のブラウザ用検索プロバイダ (IE7/Firefox2)を含むブックマーク  Google Book検索のブラウザ用検索プロバイダ (IE7/Firefox2)のブックマークコメント

Googleブック検索を使ってブラウザから検索する検索プロバイダを作りました。IE7とFirefox2の両方で動くことを確認済みです。

http://siokoshou.googlepages.com/booksgoogle.html

↑のサイトを開いて右上の検索ボックスで追加操作をしてください。

まだ日本語の書籍は少ないですけど。

[] 謎メモ  謎メモを含むブックマーク  謎メモのブックマークコメント

  • コンパイル前実行、コンパイル時実行
  • Cのマクロ、C++のテンプレート、AOP(主にJava界隈やC#界隈)、RubyのMixIn、.NETの属性による機能付加

似てるような似てないような、関係あるようなないような。

トラックバック - http://d.hatena.ne.jp/siokoshou/20070717

2007/7/16 (月)

[] 匿名メソッドって難しい  匿名メソッドって難しいを含むブックマーク  匿名メソッドって難しいのブックマークコメント

NyaRuRuさんのC#クイズを見て、そういえば前のクイズのほうもよくわからないまま放置してたなぁと思い出してしまいました。delegateまわりは推論があったり手厚いシンタックスシュガーがあったりレキシカルクロージャだったり共変と反変だのと、まぁとにかくお祭り騒ぎな機能ですよね。

using System;
using System.Linq;
using System.Threading;
using System.Linq.Expressions;

class Program
{
  delegate void Func();
  static void Main()
  {
    string s = "Hello ";
    Func f = () => { while ( true ) Console.Write( s ); };

    f.BeginInvoke( null, null );
    Thread.Sleep( 100 );

    s = "World ";
    Thread.Sleep( 100 );

    s = "!\n";
    Thread.Sleep( 100 );
  }
}

↑EndInvoke呼んでなくてスミマセン。って謝るところはそこじゃないですね。

匿名メソッドはクロージャじゃないってタイトルで話題になった件ですが、日本でも海外でもたくさんの方が解説を書いていましたね。その件のスコープの問題とはちょっと違うけど、匿名メソッドがどう展開されるかの説明がわかりやすいのは超有名blogのこちら。

http://blogs.msdn.com/oldnewthing/archive/2006/08/02/686456.aspx


クロージャが何かってのは最近のはてな内でいろいろ議論された結果、定義がはっきりしてない言葉ってことで終わったようです。やっぱりネットっておもしろいなぁと思って読みふけっていました。


あと気になるのがこちら。リソース浮くパターンがあるけど、3.0じゃ直せないらしい。でも、なんで浮くのかよくわかりません…

http://blogs.msdn.com/ericlippert/archive/2007/06/06/fyi-c-and-vb-closures-are-per-scope.aspx

Eric Lippertさん、すごい人ですねぇ。C#のあらゆるところをつつきまわしてます。blogは文章多いし、しかも濃いんで、読むのが難しいんですが、なんで同じ言語使っててこんなにいろんなところに気が付くのか不思議です。写真みつけた。http://www.amazon.com/gp/pdp/profile/A3P2N0LR8NZYI6


匿名メソッドを堪能したい方はこちらw

http://blogs.msdn.com/grantri/archive/category/3378.aspx


どこかでこの、環境の値を変えるとクロージャ内から見える値も一緒に変わってしまうのは、最初はLisp実装のバグだったと読んだような気がします。

# Expression Treeがわからないので、クイズの2問目がさっぱりorz Expression Treeの解説ってどこかにないでしょうか…

2007/7/14 (土)

[] カリー化で遊んでたら全然違うものになった  カリー化で遊んでたら全然違うものになったを含むブックマーク  カリー化で遊んでたら全然違うものになったのブックマークコメント

※最初はカリー化と称して全然違うものを書いてました。全面的に訂正しました、ごめんなさい。

C#3.0の拡張メソッドのカリー化、便利な使いどころを見つけようと遊んでいるうちにカリー化してない例になってしまったけど、現実的なLINQの使い方になっているので記録。

あらかじめLINQのクエリーを用意しておいて、whereの条件式だけ可変にする例です。

この例ではちょっと極端な例にしたかったので、where後、selectして表示する手続きまでまとめてみました。Lisp脳じゃないですねw

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
  delegate void FindAndAction( Func<Person, bool> f );

  static void Main()
  {
    var persons = new[] {
      new Person { Name = "Wataru Abe",         Gender = Gender.Male,   IsActive = true },
      new Person { Name = "Masao Sueda",        Gender = Gender.Male,   IsActive = true },
      new Person { Name = "Sae Nakarai",        Gender = Gender.Female, IsActive = true },
      new Person { Name = "Shiori Yamamoto",    Gender = Gender.Female, IsActive = true },
      new Person { Name = "Satoshi Hatakeyama", Gender = Gender.Male,   IsActive = false },
    };

    FindAndAction findAndPrintNames = f => persons.Where( f )
                     .Select( p => p.Name )
                     .ToList().ForEach( Console.WriteLine );

    findAndPrintNames( p => p.IsActive );

    Console.WriteLine();

    findAndPrintNames( p => p.Gender == Gender.Female );

    Console.ReadKey();
  }
}

public enum Gender { Male, Female }

class Person
{
  public string Name { get; set; }
  public bool IsActive { get; set; }
  public Gender Gender { get; set; }
}

匿名型を使わずPersonクラスを作ったのは、型がないとdelegateの定義が書けなかったため。もしかしたら、こないだNyaRuRuさんが書いてた記事のあたりで、どうにかなるのかもしれません。

もう一つ。Personはimmutableにしたかったけど、自動プロパティを{get;}だけにしようとしたらエラーでした。泣く泣くmutableに。{get;}とできたところでimmutableになっているのかどうかは、要検討。

NyaRuRuさんにコメントで教えてもらったToListをさっそく使っています。実験コードには超便利。NyaRuRuさんのコメントにあるようにその場で実行されたりインスタンス化されたりと、LINQの利点を台無しにしてしまうかもしれないのでご注意を。

ちなみにカリー化とは

Func<Func<Person, bool>, IEnumerable<Person>> where = persons.Where;

こんな感じ。Whereは2変数を取ります(1変数を取るインスタンスメソッドのように見えるけど)。そのうち一つ目の引数をがっちり固定して、それに名前を付けて持ち運べるってのが、C#3.0のカリー化です。もう一つの引数は開いたまま。Func<Func<Person, bool>, IEnumerable<Person>>の部分は嫌な見た目なので、delegateにしたほうがキレイですね。

ちなみに、このコードはC#3.0です、念のため(^^; もはやC#に見えないかもしれませんが…

[] 正規表現 Cheat Sheet  正規表現 Cheat Sheetを含むブックマーク  正規表現 Cheat Sheetのブックマークコメント

http://regexlib.com/CheatSheet.aspx

\b って単語境界だったりバックスペースだったりするのか。マッチしなくて使うの諦めたことがありました(T-T)

MSDNの日本語解説。コンテキストによって変わるらしい。いやらしい。

NyaRuRuNyaRuRu 2007/07/14 18:16 >どうにかなるのかもしれません。
とりあえずダサイ版だと……
static Action<Func<T, bool>> Dummy<T>(T dummy) { return null; }
var findAndPrintNames = Dummy(persons[0]);
あとは findAndPrintNames を使い回す,とか.

>ToList
あー,あれはどちらかというとちょっとネガティブ気味の引用です.ToList を呼ぶと即時評価と (場合によっては) List<T> のインスタンス生成が行われちゃいますからねぇ.
やっぱり IEnumerable<T> に対する ForEach が足りてない気がします.
http://www.base4.net/Blog.aspx?ID=422
なんか深い理由はあるんでしょうけど.

あと余談ですが,AsEnumerable や AsQueryable といった As〜系と,ToList や ToDictionary といった To〜系の違いは,前者は遅延評価されて,後者は即時評価されるという感じに使い分けられているようですね.
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1135817&SiteID=1#1135817

NyaRuRuNyaRuRu 2007/07/14 18:20 って,実験用では確かに行数短くなるのでいいですな.< ToList
とはいえ共通言語的な C# 3.0 Supplement が欲しいっす.boost とか prototype.jp みたいな.

siokoshousiokoshou 2007/07/14 18:40 どうもこの一度型付けしてあげる(?)部分がすんなり覚えれないです…。バッドノウハウとしてがんばって覚えますw
ToList、私も書き方が悪くてスミマセン。本番コード用としては使うつもりはなくて、でも実験コード用としてはお手軽でいいなぁと思ってます。ちょっと注意書きしておきます。
As系は初めて知りました。なるほど、Toはその場で評価してしまいますね。
ForEachがないのはLINQはあくまでもSQLもどきってところへのこだわりなんですかねぇ。

ShiraleeShiralee 2007/07/14 22:19 自動プロパティは {get; private set;} と書くとimmutableにできますよ。

siokoshousiokoshou 2007/07/15 00:42 お〜、ありがとうです。なしにはできないけど、privateにはできるんですかぁ。

トラックバック - http://d.hatena.ne.jp/siokoshou/20070714

2007/7/12 (木)

[] LINQ で FizzBuzz  LINQ で FizzBuzzを含むブックマーク  LINQ で FizzBuzzのブックマークコメント

IEnumerable<T>病がじわじわ広まっているので、LINQでFizzBuzzしてみた(イミフ)。

http://golf.shinh.org/p.rb?FizzBuzz

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        foreach ( string str in Enumerable.Range( 1, 100 )
            .Select( n => FizzBuzz( n ) ) )
        {
            Console.WriteLine( str );
        }
        Console.ReadKey();
    }

    static string FizzBuzz( int n )
    {
        if ( 0 == n % 15 ) return "FizzBuzz";
        if ( 0 == n % 5 ) return "Buzz";
        if ( 0 == n % 3 ) return "Fizz";
        return n.ToString();
    }
}

あんまりLINQじゃない…。でも、LISP脳と考え方が近いのでニヤリ。

こうやって遊んでみるとLINQに足りないところがみえてきますね。今のところ、SQLの羊の皮をかぶって抵抗感を和らげているけど、次の版あたりでどう化けるのか楽しみでもあり、怖くもあり…。

NyaRuRuNyaRuRu 2007/07/13 00:05 まえどこかで某氏も書いてましたが ForEach 欲しいですな.

//タブは全角スペースになってます
static class Util
{
 public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
 {
  foreach (T item in source) { action(item); }
 }
}

class Program
{
 static void Main()
 {
  Enumerable.Range(1, 100).Select(
   n =>{
    if (0 == n % 15) return ”FizzBuzz”;
    if (0 == n % 5) return ”Buzz”;
    if (0 == n % 3) return ”Fizz”;
    return n.ToString();
   }).ForEach(Console.WriteLine);
  Console.ReadKey();
 }
}

NyaRuRuNyaRuRu 2007/07/13 00:17 ↓ .ToList() をそういう用途に使うのはどうなんだろ?
http://blogs.msdn.com/lukeh/archive/2007/07/11/c-3-0-and-codedom.aspx

siokoshousiokoshou 2007/07/13 07:08 ForEach欲しいですね。ToList().HOGE() いいですね!
あと、パターンマッチが欲しいです!FizzBuzz程度ならあっても変わらないけど…。
DoxBoxが一言「パターンマッチないの?」と言ってくれれば解決するかもw

2007/7/10 (火)

[] 不変なオブジェクト  不変なオブジェクトを含むブックマーク  不変なオブジェクトのブックマークコメント

不変、immutable について。生成したら生涯、値が変わらないオブジェクトを不変なオブジェクトといいます。

string や Uri クラスなどが例です。誰もが使っている割りに、言及されることはまれです。string や Uri と対になる mutable なクラスは StringBuilder と UriBuilder ですね。

書籍なら「Effective Java」、結城浩さんの「Java言語で学ぶ デザインパターン入門 マルチスレッド編」に説明があります。詳しい解説はこれらを参照してください。私も不変初心者なので(^^;

利点を Effective Java から引用すると、「設計、実装、使用が可変クラスよりも容易」、「誤りにくく、より安全」。でも、設計、実装が容易という点はそうとも言えないかも。wikipedia 参照。これは簡単とは言えない…。単純な値を表すクラスを不変にするのは簡単かもしれないけど、複雑なクラスを不変にするのは大変、ということかもしれません。

ほかの利点は、スレッドセーフである(同期不要)、共有できる、さらに進めてインターン化できる(値の種類が少ないならとても強力かもしれない)、などなど。Effective Java では複素数型を例に、関数的だと説明しています。副作用がないってことですね。

不変な型は使う分には間違いなく簡単です。中途半端な状態がないのは気持ちいいし、何も考えなくてもスレッドセーフなのもいい感じ。

欠点は異なる値を表すのに別のオブジェクトが必要なる点だそうで、特にオブジェクトが大きいとちょっとの変更でも大量のコピーが必要になって、マズー。

外から見て不変であればいいので、内部に可変の値を持つのはありです。C++ の mutable キーワードを検索すれば、このような例が見つかります。mutable ってこのためにあったんだねぇ。


Effective Java には「可変にすべきかなり正当な理由がない限り、クラスは不変であるべきです。」と強烈なことが書いてあります。今後、検討してみる。


で、前にも書いたけど気になったきっかけはこれ。C#3.0の匿名型が immutable に変更になった。これはあるべき姿かもしれない。

オブジェクトの生存期間の途中で、ハッシュ値が変わることがマズイというわけで immutable にしたようです。しかし、Paul Vickさんはこれは匿名型だけの問題じゃないよね、と指摘Orcas 以降でほかの型もハッシュ値の求め方を変更しようと考えているようです。これまでハッシュ値をどうやって求めるか、たびたび悩んでたのでちょっと期待。ハッシュ値ってのは今も盛んに研究されてますよね。主に暗号関連のほうかもしれないけど。

Dictionary<,> の MSDN ライブラリの説明に「オブジェクトが Dictionary のキーとして使用されている場合、そのオブジェクトに対してハッシュ値に影響するような変更を行わないでください。」とあります。これまではもう変更しないオブジェクトを Dictionary に入れてたけど、そういうものを不変にすればよいわけですね。一発で作れない場合は、対応する mutable なクラスを用意すればOKです。

もう一つの気になったきっかけ。演算子 == のオーバーライドに、「型が変更できない場合、つまりインスタンスに含まれているデータを変更できない場合、その型は、変更不可能なオブジェクトとして、同じ値を持つ限り同一と見なされるので、参照の等価の代わりに値の等価を比較するように演算子 == をオーバーロードするのが有効です。変更不可能な型以外で演算子 == をオーバーライドすることはお勧めしません。」とあります。

なんで「変更不可能な型以外で演算子 == をオーバーライドすることはお勧めしません。」なのか理由がわかりません…。わかりませんが、値を表す型は不変にするべき → 値を表す型以外なら参照の等価(同一性)を調べるだけで十分 って意味なのかなぁと推測。どなたか理由がわかる方、教えてください。こうじゃないの?ってご意見も歓迎。


でも複数の型からなる型を不変にするのって、大変そう。匿名型も自身だけが不変でも、含んでいる型が不変じゃなければ変更できるんじゃないのかな?気になる。


というわけで、これからは不変性を気にしてみることにします。Effective Java、良い本ですよ、オススメです。

siokoshousiokoshou 2007/07/11 21:43 何これと思って★いっぱいクリックしてしまったorz

トラックバック - http://d.hatena.ne.jp/siokoshou/20070710

2007/7/9 (月)

[] LINQ開発秘話  LINQ開発秘話を含むブックマーク  LINQ開発秘話のブックマークコメント

それはある日の、悪名高い Don Box からの一通のメールからはじまった。「なんで expression tree に eval がないの?」

…続きは英語で。

IQueryable's Deep Dark Secret


おもしろいです、このお話。

これって、以前波村さんが書いたDLINQ Is Magicのことなのかなぁ。ちょっと時期が違うけど大きくは違わないので、これかもしれない。

# Don Box って悪名高かったのかw

トラックバック - http://d.hatena.ne.jp/siokoshou/20070709

2007/7/8 (日)

[] 拡張メソッド  拡張メソッドを含むブックマーク  拡張メソッドのブックマークコメント

利点

  • クラス提供者ではなく利用者が、クラスを後から拡張できる
  • Mix-inとはまた違う、クラス横断型のクラス拡張方法 (としても使える)
  • パイプ&フィルタ(例:LINQ)がネストした関数呼び出しから、左から右へと続く一連の関数の羅列になり、いかにもパイプらしく見える

欠点

  • 濫用は間違いなく混乱の元
  • どのメソッドが呼ばれるのか複雑になる
  • バージョン管理の問題も出てくる

C#3.0は関数型のエッセンスを取り入れたとよく言われるけど、むしろLL方向に近づいていると思います、ハイ。RubyとC#3.0はとても似ていると思います。LL方面の人がC#3.0を見れば違和感は感じないかもしれないけど、JavaやC#2.0方面の人が見ればこんなのありかよっと感じる人が多そう、とか最近考えていたり。


Rubyでやってみる

class String
  def hello
    print "Hello ", self, "\n"
  end
end

"C#3.0".hello

Stringクラスにhelloメソッドを追加。結果は「Hello C#3.0」。

LLに近づいてるよね、やっぱり。

トラックバック - http://d.hatena.ne.jp/siokoshou/20070708

2007/7/1 (日)

[] IronPythonの名前の由来が「料理の鉄人」だった件について  IronPythonの名前の由来が「料理の鉄人」だった件についてを含むブックマーク  IronPythonの名前の由来が「料理の鉄人」だった件についてのブックマークコメント

http://blogs.msdn.com/shozoa/archive/2007/06/30/python-workshop-the-edge-2007.aspx

コーヒー吹くとこだったw

ずっと気になってたので、この情報はうれしい

トラックバック - http://d.hatena.ne.jp/siokoshou/20070701
2005 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 06 | 09 | 11 | 12 |
2007 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 08 | 09 | 10 | 12 |
2009 | 01 | 03 | 04 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 07 |
2011 | 04 | 07 | 10 |
2012 | 04 | 12 |
2013 | 08 |
2014 | 03 | 08 |
2017 | 09 |