シンプルで高速な構文解析ライブラリ「Parsec」を.NETで使う^^

Parsecって何?

Parsec 高速なコンビネータパーサ

Parsec は Haskellモナドパーサコンビネータライブラリで、文脈依存文法や無限先読み文法を解析できますが、 LL文法と同等の性能を出します。
コンビネータパーサはプログラムの他の部分と同じプログラミング言語を使って書かれます。文法の形式化 (Yacc) とそこで使われるプログラミング言語 (C) のギャップがありません。ユーザはたったひとつの言語を学ぶだけで済みます!


参考参考
2004-07-30
http://alohakun.blog7.fc2.com/blog-entry-654.html
ライブドアブログ(livedoor Blog)| 読みたいブログが見つかる

これは魅力的><
僕が今欲しいのはまさにこれじゃないか!!!

で、いろんな言語に移植されてるみたいです。

.NETでも使えるやつ

NParsec、C#で使えるよー NParsec+Tutorial

FParsec、F#で使えるよー FParsec Documentation

さて、どっち使うか?

って、考えるまでもなく、FParsecですねw

NParsecのソースだと、魅力は半減(C#3.0だと少しは違うんでしょうけど、まぁ、無理やりですよね)

英語読めないので、機械と人の手を借りて読みましたが、
この先は誤った解釈などがあるかもしれませんが、悪しからず^^;

Introductionから

  • FParsecはDaan LeijenさんのHaskell用パーサコンビネータライブラリをF#に移植したもの。
  • Unicode、4GBを超える大きなファイルに対応してます。
  • 設計、実装は最適化されてて、大きな要求も十分な性能をもたらす。
  • FParsecはBSDライセンス

FParsecはコンビネータでできてる

コ、コンビネータのことは、Y Combinatorが凄すぎる! - yuji1982の日記の件で調べたけど、何とか理解した程度。。

SとかKとかI、、ですよねー><

と、濁しておしまい。

実際、活用してるかっつーと、全くしてなかったけど、でも、パーサコンビネータ見ると、コンビネータっておもしろいなー、と思う

環境構築から

必要なもの

準備手順

  1. FParsecCSとFParsecをビルド。
  2. F#のプロジェクトを追加して、参照パスに"-I ..\\..\\Build\\Debug -R FParsecCS.dll -R FParsec.dll"を追加。※-Iのパスは上の二つのdllがある場所
  3. F#のソースには、以下が必要
#light
open FParsec.Primitives
open FParsec.CharParser

これでOK。。と。

では、FParsecの勉強開始

パーサコンビネータは一つもしくは複数のパーサを引数として取り、「結合された」パーサを返す関数です。とな。

まさに、コンビネータってやつですね!

で、これが基本形のようです。

type GenParser<'a,'s> when 's :> IState = 's -> Reply<'a,'s>

「FParsecのすべての基本コンビネータと派生コンビネータは、この型のパーサが引数になる」

んー、わかるけど、、具体的に使わないとピンと来ないな^^;

結果はこんなやつらしいです。

type Reply<'a, 's> =
ConsumedOk of 'a * 's * ParserError
ConsumedError of ParserError
EmptyOk of 'a * 's * ParserError
EmptyError of ParserError
実際は、こういうunion typeじゃなくて構造体らしい。(パフォーマンスのため) でもアクティブパターンがあるから、このunion typeのように使えるらしい。 なるほどねー^^

では、早速具体的な例から

type Parser<'a> = Parser<'a,unit>

let simple: Parser<_> =
    parse {let! c = upper    
           do!  skipChar '#'
           return c}
ふむふむ、、これを呼んでみる。
let main()=
    print_any(run simple "F#")
    System.Console.ReadKey(false) |> ignore
結果 Success 'F' なるほど simpleってパーサに"F#"を渡すと、結果型のFになる。 この結果は、charパーサの結果型らしいです。
type ParserResult<'a> =   Success of 'a
                        | Failure of string * ParserError
つまり、upperって言う大文字一文字のパーサと、skipCharって言う(たぶん検査だけして取得しない?)パーサーを 合成(結合?)して、simpleってパーサを作ったわけですね! で、このupperのような定義済みパーサを組み合わせて行くわけですね。
エラーにすると?
run simple "C++" にしてみる。 結果
= Failure
Error in Ln: 1 Col: 2
C++
 ^
Expecting: '#'
なるほどなるほど
ちょっとだけ、じっくり見てみる
let simple: Parser<_> =
    parse {let! c = upper    
           do!  skipChar '#'
           return c}
このparse {}って書き方は何でしょう?よく見ると、letじゃなくてlet!ですし、do!ってのもありますね。 これはcomputation expressionsと言う、シンタックスシュガーのようです。
let parseXY = parseX >>= fun x ->
              parseY >>= fun y ->
              preturn (x, y)
こんな書き方を、
let parseXY = parse {let! x = parseX
                     let! y = parseY
                     return (x, y)}
こうできるらしいです。
computation expressionsの前に元のやつを、もうちょっとだけ見てみる
まず単純な文字パーサに代えて考えてみる
let parseXY = parse {let! x = anyChar
                     let! y = anyChar
                     return (x, y)}
文字を二つ(xとy)タプルで返すパーサになります。 結果は val it : ParserResult = Success ('a','b') これを元の形で考えてみる。
let parseXY = anyChar >>= fun x -> anyChar >>= fun y ->preturn (x, y)
>>=とpreturn >>=  val (>>=): GenParser<'a,'s> -> ('a -> GenParser<'b,'s>) -> GenParser<'b,'s> preturn  let preturn x = fun state -> EmptyOk x state NoError >>=でパーサーを結合して、最後に結果を取るためのパーサpreturnを使ってるってことですね。 んー、わかるにはわかりますが、、ややこいです。 そこでcomputation expressionsに頼るわけですね。
computation expressionsはモナドライク!?
let parseXY = parse {let! x = anyChar
                     let! y = anyChar
                     return (x, y)}
わかり易くこう書けるのは、computation expressionsのおかげなんですが、これは何なのか? いげ太センセーが紹介してくれてます。 http://igeta.cocolog-nifty.com/blog/2008/02/computation_exp.html http://igeta.cocolog-nifty.com/blog/2008/04/maybe.html http://igeta.cocolog-nifty.com/blog/2008/04/f_monad.html 僕はまだイマイチ理解できてませんが、モナドライクなわけですね。 とりあえずは、、楽にしてくれる便利なものってことで^^;

ファイルから入力して解析する方法

runParserOnFile simple () "test.txt" System.Encoding.Default

FParsecのパーサーをいろいろ見る

FParsec Documentation 文字系 いろいろあるんですねー
 char, anyChar, anyCharOrNL, upper, lower, letter, digit
 newline, unicodeNewline
 whitespace, spaces, unicodeWhitespace
 satisfy
 string anyString
 regex
 manyChars, manySatisfy, manyCharsTill, manyTillString
 skipped 
いくつか試してみる
run (char 'a') "abc";;
val it : ParserResult = Success 'a'

run (anyChar) "abc";;
val it : ParserResult = Success 'a'

run (string "ab") "abc";;
val it : ParserResult = Success "ab"

run (satisfy (fun c->c<>'a')) "bcd";;
val it : ParserResult = Success 'b'

run (anyString 4) "abcde";;
val it : ParserResult = Success "abcd"
選択 <|>
run (char 'a' <|> char 'b') "abc";;
val it : ParserResult = Success 'a'

run (char 'a' <|> char 'b') "bc";;
val it : ParserResult = Success 'b'
choice
run (choice [(char 'a');(char 'b')]) "abc";;
val it : ParserResult = Success 'a'
choice [p1; p2; p3; ...] は p1 <|> p2 <|> p3 <|> と同じ。 でもchoiceの方が効率がいいらしい(?) .>>、>>. .の付いてる側を取得して、付いてない方は消費するだけ?でいいのかな
run (char 'a' .>> char 'b') "abc";;
val it : ParserResult = Success 'a'

run (char 'a' >>. char 'b') "abc";;
val it : ParserResult = Success 'b'
とりあえず、おk 繰り返し many、skipMany
run (many digit) "123abc";;
val it : ParserResult = Success ['1'; '2'; '3']
なるほど、数字を複数処理したわけですね、、と言うことは間に文字を入れると、、
run (many digit) "12a3bc"
>val it : ParserResult = Success ['1'; '2']
うんうん、おk ※前から思ってるけど、カナ打ちの僕が「おk」って、、違いますよねw
run (skipMany (char 'a') >>. char 'b') "aaaabc";;
val it : ParserResult = Success 'b'
manyTill、skipManyTill
run (manyTill (char 'a') (char 'b')) "aaaabca";;
val it : ParserResult = Success ['a'; 'a'; 'a'; 'a']

run (skipManyTill (char 'a') (char 'b')) "aaaabca";;
val it : ParserResult = Success ()
おそらくbは消費してますよね?
run ( (skipManyTill (char 'a') (char 'b') ) >>.char 'c') "aaaabca";;
val it : ParserResult = Success 'c'
うん、おk manyと文字の組み合わせ manyChars、manySatisfy
run (manyChars (char 'a')) "aaba";;
val it : ParserResult = Success "aa"

run (manySatisfy (fun c->c<>'z')) "abczdef";;
val it : ParserResult = Success "abc"
セパレータで区切られた解析 sepBy, endBy sepEndBy
run (sepBy (anyString 2) (char ';')) "ab;cd;ef";;
val it : ParserResult = Success ["ab"; "cd"; "ef"]

run (sepEndBy (anyString 2) (char ';')) "ab;cd;ef;";;
val it : ParserResult = Success ["ab"; "cd"; "ef"]
sepEndByは最後にセパレータがあっても良い。sepByだとエラー。 run (endBy (manyChars (satisfy (fun c->c<>';'))) (char ';')) "abcdef;";; val it : ParserResult = Success ["abcdef"] パイプライン
>>
run (digit |>> fun c -> Char.code c- Char.code '0') "1";; val it : ParserResult = Success 1 消費しないパーサ lookAhead、followedBy、notFollowedBy
run ( (lookAhead (string "ab") ) >>. (manyChars anyChar) ) "abc";;
val it : ParserResult = Success "abc"
追記 between
> run (between (char '(') (char ')') (regex "[^)]+")) "(abc)";;
val it : ParserResult = Success "abc"

脳が痛がってるので、今回はここまで>< (たぶん追記します)

まだ理解してないものメモ chainl1

最後に注意!!

FParsec Documentationで、以下の"ような"ことが書かれてるので注意です! このページのFParsecは未リリース版です。 かなり総合的なテストは行われてますが、まだバグがあると思われるので、 もし製品環境で使う場合は、徹底的にテストする必要がありますよ。

WPFで「検索結果のハイライト表示機能」ってどう作ればいいんだろう?

少し前から考えてるけど、いまいちピンと来ない。

ドキュメントページに限らず、○○(商品などやブクマ)リストのページとか、検索結果ページとか、いろいろな場面での利用を想定した、ハイライト機能が欲しい。

ブラウザベースの世界ではあたりまえのあれ。

HTMLの世界から想像してみる (実際は知らないことばかりなので、想像ですw

クライアントサイドで、HTMLやCSSををブラウザが直接いじるとか、クライアントサイドスクリプトでいじるとか?

* Firefoxで言えば、ページ内検索の「すべて強調表示」の状態
* Googleツールバーのように、拡張でやってもいいし。

HTML生成時に埋めちゃうとか?(キーワードだけ埋めて、あとスクリプトで↑と同じでもいいけど)

* はてブの検索の結果ページとか(実際、どうやってるか知らないけど、、HTMLジェネレート時でもできるっちゃできると思っただけ)

で、これをWPFでどうやって実装しようか。。。

何に対して?どうやって?

Style、バインド、マルチバインド、Converter、カスタムコントロール、、、んー、どうしたもんか

stringのデータテンプレート?

・・・

Inlineのテキストに対して、検索ワードとマルチバインドでboolにして、トリガでバックカラー変える?でも検索ワードどうやってあてる?

・・・

Adorner使うと、、いやいや

・・・

いろいろ考えてたら混乱><

↑は全部適当です、うろ覚えで書いてます

とりあえず、いろいろ実験してみよう

何か良いアイデアとか情報ないでしょうか?^^;

.NETアプリに、ウェブ風の簡易検索やGMailフィルターのようなものを実装してみる

F#とC#で、簡易検索用の動的LINQ生成をやってみました。

こんな風に使います。


//適当なデータ
var data = new[]{
new { Name="yuji1982",Age = 26,Like="F#,Visual Studio", Man=true, Luckyday = new DateTime(2008,1,1) },
new { Name="amachang",Age = 26,Like="JS,javascript,1000sp",Man=true, Luckyday = new DateTime(2008,7,1)},
new { Name="adeton",Age = 20,Like="C#",Man=true, Luckyday = new DateTime(2008,12,1)},
new { Name="zio3",Age = 20,Like="C#,x-box360,XNA Game Studio,Silverlight",Man=true, Luckyday = new DateTime(2008,8,2)},
new { Name="no-0",Age = 126 ,Like="Visual Basic, Expression Studio",Man=false, Luckyday = new DateTime(2008,10,3)},
new { Name="no-1",Age = 0 ,Like="a26a",Man=false, Luckyday = new DateTime(2008,2,1)},
new { Name="no-2",Age = -9,Like="abc",Man=false, Luckyday = new DateTime(2008,3,15)},
new { Name="no-3",Age = 99,Like="Visual Studio",Man=false, Luckyday = new DateTime(2008,12,1)},
new { Name="no-9",Age = 10,Like="C言語",Man=false, Luckyday = new DateTime(2008,4,1)},
new { Name="no-10",Age = 21,Like="javascript",Man=false, Luckyday = new DateTime(2008,4,2)},
new { Name="no-11",Age = 27,Like="C#",Man=true, Luckyday = new DateTime(2008,4,2)},
new { Name="no-12",Age = 23,Like="C",Man=false, Luckyday = new DateTime(2008,4,3)},
};

//クエリ検索の実行
var result = data.Search("(C# or F# or javascript) 20..30 2008/1/1..2008/10/1 Name:-no 1");

//結果を表示してみる。
foreach (var item in result)
{
Console.WriteLine(item);
}


結果
{ Name = yuji1982, Age = 26, Like = F#,Visual Studio, Man = True, Luckyday = 2008/01/01 0:00:00 }
{ Name = amachang, Age = 26, Like = JS,javascript,1000sp, Man = True, Luckyday = 2008/07/01 0:00:00 }

このクエリは以下の内容のand検索になっています。

・どこかにC#かF#かjavascriptが含まれる
・数値が20から30の範囲 (数値型プロパティのどれでも)
・日付が2008/01/01から2008/10/01 (DateTime型プロパティのどれでも)
・Nameにnoが含まれない
・数値が1か文字列に1が含まれる

どうやって動いてるか


検索クエリ

解析(Fsyacc/Fslex)

AST(F#)

Expressionに変換(F#) ← 検索対象指定用のデータ(プロパティ名一覧)

LINQで使用

これをC#でラップして呼べるようにしただけです

何に使えるか?

  • 簡易ツールに使える 

 とりあえずは、簡単なツール(クライアントツール)とかで使えるかなーと思う。
 自分用はてブツールとか、twitterクライアントとか、、パフォーマンスが要求されない部分ならとりあえずあれば便利?

  • Linq to SQLにも使える(たぶん)

 まだ、簡単なパターンのテストしかできてないけど、単純な式ツリーだから、大丈夫だと思う。
 Linq to Object、Linq to SQLの両方に気軽に使えるのが、LinqのExpressionで構築する一番の良さだと思ってる。
 と、言うことで、データベースにも使えるので、webサービスでも使えますね^^

  • GMailフィルターのようなものを簡単に作れる

 文字列に名前を付けて保存するだけで、あの便利なGMailフィルターを真似できますよね^^

 引数が文字列一つで済むから楽>< (この理由はLinq to RESTがリリースされれば関係ないけどw)

クエリに使える演算子について

演算子 説明
AND and & AND(かつ)検索 C# F#
C#とF#含まれる
※スペースを入れてもANDになる
OR or | OR(または)検索 C# or F#
C#かF#が含まれる
()
(括弧)
グループ化 (C# or F#) (2005 or 2008)
Not not ! 否定 !C#
C#が含まれない
-
(ハイフン)
除外 -C#
C#が含まれない
※基本は否定と同じ、違いはグループ化した時の動作
-(a b)は!(a or b)と同じ
数値の前に使うとマイナスとして判断されるので-(9)などにする必要がある
ターゲット名: 検索対象のプロパティを指定する
ターゲット名はプロパティ名の別名として設定できる
ターゲットを指定しないものはAllとして設定したプロパティ全てが対象になる。
名前:yuji
Nameプロパティにyujiが含まれる
""
(引用符)
フレーズ検索 "Visual Studio"
"x-box"
とかに使う。
また、数値9を文字"9"にするなど
{}
(中括弧)
ORでグループ化する {C# F#}
(C# or F#)と同じ
.. 範囲指定 20..30
20から30
※数値か日付にのみに使える。
< > <= >= 関係演算子(って言うのかな^^;) <20
20未満
is:ターゲット名 bool演算 is:Man
Manプロパティがtrueかどうか

数値と日付の扱いについて

  • 数値と文字の違い

 数値9で検索すると、9が含まれる文字列と数値9にヒットする。
 文字"9"で検索すると、9が含まれる文字列と9が含まれる数値(19,-90とか)にヒットする。

  • 日付のフォーマット

 yyyy/MM/ddとyy/MM/ddを想定してる(Fslexの正規表現がうまくいってないので、適当ですが・・・)
 yy/MMやMM/ddって対応したいけど、どうやろうかなー

課題

おわり

F#の練習のつもりでやってたから、コードがぐちゃぐちゃ^^;コメントになってるところがありすぎるw
ソースはあとでうpします。とりあえず、dll上げときますので、試してみる方はどうぞ^^

(追記)ソース晒しておきます。

ソースダウンロード(DynamicSearch.zip)
DLLダウンロード(DynamicSearchDLL.zip)

※.NET Fx3.5とF#が必要です。

F#のダウンロードは以下のページから。ライセンスもこのページから見てください。
http://research.microsoft.com/fsharp/release.aspx

F#のDLLが数個あればいいので、zip版からサクっと使うだけでいいんだけど、
非商用なら、一緒に配っちゃってもいいのかなー・・・(必死で読んでもよくわからん^^;

Fslexは量指定子使えない?

量指定子が使えない(と思う)

なぜか以下はコンパイルは通るけど(実際使えるかどうか、どう動くかは未検証)

['0'-'9']{2,4} { ... }

以下はエラー

['0'-'9']{2,4}['0'-'9']{2,4} { ... }

当然、以下の二つもエラー

let date= ['0'-'9']{2,4}


let digit = ['0'-'9']
let date = digit{2,4}

量指定子ないのは辛すぎる><

と言うか、正規表現が全般的に使いづらい><

.NETと同じ先読みと後読みがない(?)のも辛い。
マクロ定義のようなことできないのもめんどい・・

もっとスマートに書く方法はないのかなー

何より

[^' ' '\t' '\r' '\n']

これはやっぱり

[^ \t\r\n]

こうだよね(´Д`)

C#3.0の匿名型を使ってて、ふと思ったこと


匿名型はこんな風に書ける


var me = new { Name = "yuji", Age = 26, Like = "C#" };
プロパティ名を書いて、値を指定するだけ。

プロパティ名は省略できる。
省略すると同じプロパティ名になる。


var me2 = new { Name = me.Name , me.Age };//Ageが省略したやつ

もちろん、ローカル変数でもおk


var name = "yuji";
var me3 = new { name };

だったら、こうしたくない?


var Name = "yuji";
var me4 = new { Name };

ローカル変数をPascal 形式で書きましたが何か?(^ω^)

駄目ですか、そうですか……

Select文の中とかで書きたくならないですか?


var data = new[]
{
new { Name = "yuji", Age = 26, Life = 100},
new { Name = "adeton", Age = 3, Life = 9999 }
};

var sum4 = data.GroupBy(d => d.Age/10)
.Select(gp =>
{
var Life = gp.Sum(d => d.Life);
return new
{
Life,
Title = string.Format("{0}代は、合計{1}です。", gp.Key*10, Life),
};
});

ないですか、そうですか、そうですね、名前書くぐらいサボんなって話ですよね。

そもそも、同じ名前にならない事の方が多い(正しい)ですよね。(↑だとTotalLifeとか?)

LINQでたくさん帳票作ってたら、少しでも手抜きしたくなっただけですorz

Google Bookmarksとはてブの差分を出してみる。その1

Google Bookmarkとはてブ両方使ってるけど、全部はてブに移そうと思ったので(思いつきで特に理由もないけど)
差分を出してみることに。

F#の練習に丁度いいと思ったけど、フレームワークを使うのに慣れてないので、まずはC#でやってみる。

データを取得

C#ではてなブックマークのポストができなくて参ってる - ブログ執筆中を参考に。


public static string Dump(string username, string password)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://b.hatena.ne.jp/dump");
req.AddHeader(username,password,"GET");

var res = req.GetResponse();
var sr = new StreamReader(res.GetResponseStream());
var xml = sr.ReadToEnd();
return xml;
}

とりあえず、細かいことは気にせず適当。AddHeaderは割愛^^;

やり方がわかりませんorz
いろいろ追ったら混乱してきたので、今度やることにしてやめました。
とりあえず、手動で取得して次に進む><;

まず、googleからパースしてみる

はてブxmlだけど、googleはhtmlだから面倒だなー……と思いつつ正規表現で遊びながらウニウニやるのは意外に楽しい^^

google bookmarksのフォーマットはこんな感じです。


<DT><H3 ADD_DATE="1198640344791968">Wiiリモコン</H3>
<DL><p>
<DT><A ADD_DATE="1198640899163631" HREF="http://japanese.engadget.com/2007/12/22/wii-opera-sdk/">Wii インターネットチャンネル 非公式SDK公開 - Engadget Japanese</A>
<DT><A ADD_DATE="1198640546058701" HREF="http://www.nintendo.co.jp/wii/topics/interview/vol4/ailive.html">AiLive LiveMove 紹介ムービー - Wii</A>
<DT><A ADD_DATE="1198640795500908" HREF="http://japanese.engadget.com/2007/12/20/multi-touch-pen-tablet-using-wiimote/">Wiiリモコンで今度はマルチペンタブレット - Engadget Japanese</A>

ソースはこんな感じになりました。


var regex = new Regex("(<H3.+>(?<tag>.+)</H3>)|(<A +ADD_DATE=\"(?<utc>[0-9]{13}).+\" +HREF=\"(?<url>[^\"]+)\" *>(?<title>[^<>]+)</A>)", RegexOptions.Compiled);
var matchies = htmlTextLines
.Select(str => regex.Match(str))
.Where(m => m.Success);
return matchies.ScanSelect(
string.Empty,
(match, tag) =>
Tuple.Create(
new
{
Tag = tag = match.GetGroup("tag") ?? tag,
Date = match.GetGroup("utc").ParseLong().IsNotNull(l=>l.ToUtc()),
Url = match.GetGroup("url"),
Title = match.GetGroup("title"),
},
tag)
)
.Where(bm => bm.Url != null)
.GroupBy(bm=>bm.Url)
.Select(gp=>
new BookMarkBase.BmItem
{
Tags = gp.Select(bm=>bm.Tag).ToArray(),
AddDate = gp.First().Date ?? DateTime.MinValue,
Url = gp.Key,
Title = gp.First().Title
})
.ToArray();

短くしたいので、適当に拡張メソッドを作る。

  • GetGroups

Matchから結果取り出す、失敗したらnullにしてるだけ

  • ParseLong

TryParseして失敗したらnullにするだけ。だから結果はNullable

  • IsNotNull

nullじゃなかったら、結果を受け取って変換する。nullならnull返すから結果はNullable

  • ToUtc

longからUtc作ってる
意外だったんですが、フレームワークにないんですね><
あと、google bookmarkのADD_DATEがマイクロ秒(?)なのを知らずに何度も失敗しました。(この情報ってどこにあるんでしょう??)
TimeSpanを作るのに、ミリ秒の方が楽だったから13桁で切って手抜き……

ScanSelectってのを作ってみたが……いろいろ微妙

タグをどう扱うか

一行ずつ見るにはLinqのSelectじゃ扱えないので、F#のscanを真似することに。(そもそもscanが適切かはすごく微妙ですが^^;)

で、scanで、いいかなーと思ったけど、型を推論させるために、最初に匿名型を入れるのが意外に面倒……

結果の値と、次でも使う値(タグ)を分けることに

既に、scanの概念からズレてるのに、強引にこういうことするの良くないのか悩む^^;

分ける方法は、配列か、匿名型か……

配列は何となく嫌。匿名型にすると、推論の問題で逆戻り。

こうなってくると、やはりTupleが欲しい。

C# 3.0 Supplemental Library: Achiral - NyaRuRuが地球にいたころから頂きました

NyaRuRuさんのAchiralは本当にいろいろと参考になります^^
(あれもこれも真似しまくってます>< Ms-PLありがとうございます>NyaRuRuさん)

で、Tupleを生成するときも型を書きたくないので、Createメソッド作って推論させる。

やっぱTupleがあるし、F#の方がいいなー、なんて思ったり。。

ただ、C#で書いたクラスの呼び方はあまり好きじゃないけど・・・
F# at Microsoft Research - Microsoft Research
落とし所としては、問題ない(むしろよくできてる)んだろうけど、やっぱりちょっと違和感が・・・

はてブをパースしてみる。

xmlはこんな感じ。


<entry>
<title>応答: Speech Server 2007 を使用した音声応答ワークフロー</title>
<link rel="related" type="text/html" href="http://msdn2.microsoft.com/ja-jp/magazine/cc500549.aspx?pr=blog" />
<link rel="alternate" type="text/html" href="http://b.hatena.ne.jp/yuji1982/20080424#bookmark-8354393" />
<issued>2008-04-24T19:37:39+09:00</issued>
<id>tag:hatena.ne.jp,2005:bookmark-yuji1982-8354393</id>
<summary type="text/plain">Speech Server 2007とWFでごにょごょ。まーやんないだろうけど・・・</summary>
<dc:subject>Speech Server 2007</dc:subject>
<dc:subject>WF</dc:subject>
</entry>
ソースはこれだけ


var doc = XDocument.Load(new StringReader(xml));
var ns = XNamespace.Get("http://purl.org/atom/ns#");
var dc = XNamespace.Get("http://purl.org/dc/elements/1.1/");
return doc.Descendants(ns + "entry").Select(
el =>
new BmItem
{
Title = el.Element(ns + "title").Value,
Url = el.Element(ns + "link").Attribute("href").Value,
AddDate = DateTime.Parse(el.Element(ns + "issued").Value),
Tags = el.Elements(dc + "subject").Select(te=>te.Value).ToArray()
}
)
.ToArray();

XMLは楽でいい><

と言うより、

やっぱりLINQ凄い!そしてC#はいい言語><

あとやること

  • とりあえず差分出す→どうやって視覚化しようかな→WPFを忘れてしまってるので、練習に丁度いいかなー
  • F#で書いてみる
  • プログラムからGoogle Bookmarksを取得したい

飽きたらやめるかもしれないけど^^;

Visual Studio2008ではDataSetとTableAdapterは別プロジェクトにできる

できるべきなのに何でできないのかなー、と思ってたら2008からできたんですね><;*1

何か怪しいプロパティあるなーとは思ってたのに前回の仕事じゃ気づかなかったなーorz

試してみました

簡単><b

これのメリット!


partialクラスは当然エンティティのプロジェクトに追加されます!

これにメソッド追加すれば楽ですね^^

おまけ

「この機能とpartial、サービスが参照DLL内の型を利用する機能」などの機能がなくて、どうしていいかわからずに、失敗してるのを見た過去の例
※ないなりの設計ができてないと言う意味です^^;

第三位 サーバー側とクライアント側で同じロジックを実装ある。

第二位 サービス参照で生成したデータセットソース内の名前空間を都度変更して同じ型とする。
#.NETの型の決定を悪用…

第一位 サービスで設計されているのをなかったことにして、クライアントからこっそりデータベースにアクセス
#たまたま運用がイントラになったからとか…



こんなソースは二度と見たくない><

早く2008が普及するといいなー^^

*1:もしかして2005からあったり・・・ないよね?