菊池 Blog

続・菊池 和彦の足跡


AILight Blogs

目次

Blog 利用状況

ニュース

BlogRollするぐらいならトラックバックしてこい。

記事のカテゴリ

過去の記事

カテゴリ

2010年2月9日 #

←の連絡をするは機能していません

なのでそこから何かしてもらっても全く反応できませんのでご了承ください。

以上、某所よりクレームを頂いたのでお断りまで。

 

posted @ 21:41 | Feedback (0)

2010年1月18日 #

社員採用が通年化されました

また採用広報です。

株式会社クロスワープ/CROSSWARP Inc. (titleちゃんとしてねーなー、うちの会社)

ってわけで、採用ページが上がっています。

基本的に通年採用って事で締め切りとかは特に無しですので、腕に覚えのある方はどうぞ。

募集要項の選考プロセスにある通りでプログラムの書き問題がありますのでご了承ください。

posted @ 17:16 | Feedback (1)

2010年1月10日 #

[ASP.NET]デザイン時に web.config での定義を使えるようにする

表題の物、おすそ分け。

using System.Configuration;
using System.Web.UI;
using System.Web.UI.Design;

namespace System.Web.UI
{
    public static class ControlExtender
    {
        public ConfigurationSection GetConfigurationSection( this Control control, string sectionName )
        {
            if( control.Site.DesignMode ) {
                var webApp = control.Site.GetService( typeof( IWebApplication) );
                return webApp.OpenWebConfiguration( true ).GetSection( sectionName );
            }
            // デザインモードでないから通常の ConfigurationManager を使う
            return (ConfigurationSection)ConfigurationManager.GetSection( sectionName );
        }
    }
}

IWebApplication の RootProjectItem からIProjectItem をゴニョゴニョすると開発環境で認識できてるはずのものを列挙できるとか、固定の場所にある事が前提なら GetProjectItemFromUrl してPhysinalPath を取ればデザイン時にゴニョゴニョできるとか言わない。

posted @ 21:04 | Feedback (1)

2009年12月19日 #

超巨大なデータの中から指定要素を定数時間で削除する

巨大なデータってありますよね、それこそ扱うのに困るような奴が。

IDictionary< TKey, TValue > を経由してそいつにアクセスしているとして、 IDictionary< TKey, TValue >.Remove(TKey key)の呼び出しには非常に高いコストが付きまとう事を前提とした場合に、こいつを瞬間、定数時間で削除するって話。

public class RemovedState< TKey, TValue > : IDictionary< TKey, TValue >
{
    IDictionary<TKey,TValue> _original;
    TKey _removedKey;
    public RemovedState( IDictionary<TKey,TValue> original, TKey removedKey )
    {
        _original = original;
        _removedKey = removedKey;
    }
    // IDictionary の実装
    bool ContainsKey( TKey key )
    {
        if( _removedKey==key ) return false;
        return _original.ContainsKey( key );
    }
    …続く

ContainsKey の実装で解りますよね、何をやろうとしているかは。

要するに削除したって知ってるから「ねーよ」と言うクラスを間に挟む。それだけ

 

なんだよ!って思う人もいるかもだけど、このアプローチ、アトミック性とトランザクション性を合わせ持つ事に注意。

IDictionary<TKey,TValue> storage があるとして、

var tran = storage;
tran = new RemovedState( tran, key );
tran = new UpdatedState( tran, key2, value1 );
tran = new InsertedState( tran, key3, value2 );
storage = tran;

storage = tran がすなわちコミット、tran をstorageへ書き戻さずに捨ててしまえば無かった事になる(ロールバック)し、書き戻しの瞬間までは更新は他には見えないし、見える様になる瞬間においてすべての更新は整っているって言うアトミック性を持っているわけ。

var tran = storage の行の直後で var original = tran; しておき、 storage = tran の直前で original==storage のチェックをすると更新競合も検出できる(マルチスレッド前提ならもちろんの事だがロック、チェック、アップデートの順で実行してくれたまえ)

単純にこのまま行くと更新が溜まって行くにつれて次第にスタックが深くなるっていう面はあったりするが、複数の更新を保持して更新されたはずの状態で結果を返すクラスを作れればスタックの深さは制約できるよね。

んなわけで、簡単なトリックで巨大な readonly の元データをさも更新したかの様に見せられ、それをトランザクション性やアトミック性をもって実行できるのよってお話でした。

 

さて、Blogってウサも晴らしたしExcel データをコピペする仕事に戻るか。

posted @ 16:48 | Feedback (2)

2009年12月9日 #

using の謎

といっても IDisposable のほうで無くて、type aliase のほう

using Hoge = string;

error CS1041: 識別子が必要です。キー ワードは
        'string' です

using Hoge = System.String;

compile success

 

なんで?

posted @ 13:43 | Feedback (2)

2009年11月27日 #

SaveChanges って嫌い

ってか、ChangeSet を元にする更新が嫌い。DataSetとかLINQ to SQLやEntity Framework とかとか

なんでかって言えば更新処理ってトランザクション処理の主作用だよね、何と何と何を更新して、なんか挿入してなんかを削除するってのは明示的にしたい。どこでどんな更新がかかったのか解らん物のChangeSet抽出して書き戻すとか信じらんないという自分。

昨今のフレームワークの殆どがChangeSet抽出からの書き戻しなんだけど、なんででしょ。自分は絶滅危惧種?

ChangeSet抽出から書き戻しの途中でエラーになったらどうするの?DBへの更新をロールバックするのは当然としてメモリ上にあるデータは書き戻せないからポイ以上の扱いってできないよね。

売れたから在庫減らす なら 「Update 在庫 set 在庫数=在庫数-販売数 where id=売れた物 and 在庫数>=販売数 」で更新行数が 1 ならOK、ChangeSet抽出なんてもんをベースにするから 「select 在庫数,タイムスタンプ from 在庫 where id=売れた物 」で取った物をメモリ上で色々なんか解らん操作されて求まった在庫数を 「update 在庫 set 在庫数=新在庫数 where id=売れた物 and タイムスタンプ=旧タイムスタンプ」なんて隙がありすぎるオペレーションになり、結果としてロック競合しました!残念でしたって結果になる。

楽観的同時実行制御は何が楽観かといえば競合はめったに起こらないって前提なわけで、それに基づいた処理ばっかりのフレームワークって、競合起こる前提の場合ってスコープ外だよね。eコマースサイトの99%の商品が前提としてそんなに売れないよでも、売上の主役となる商品が一個あってその一個の売り上げが命運を握ってるなら楽観的ロックなんて使っちゃだめなんだよね。

昨今の楽観的ロックまんせーなフレームワークオンパレードな状況に悲観的な今日この頃であります。

posted @ 10:44 | Feedback (0)

2009年11月19日 #

メモリ帯域とCPUキャッシュの効率が問題だ

http://social.msdn.microsoft.com/Forums/ja-JP/csharpgeneralja/thread/94173254-e79c-47ad-8fbd-98c421aa74d9/

の問題がなかなか面白い。

ループ回数が膨大=i,j の軸が結構長いと仮定すると

多分 CPU のキャッシュが実質として効いてない(効かない)のが最初の問題で、メモリ帯域に率速してしまっているのが性能問題の主因だろう。

変化は一方向に伝搬しているように見える(i を更新して i-1 で見てる、j+1を元にjに反映)のでブロックごとに演算して CPU キャッシュを効かせてあげる&依存関係から外れる所を並列化の対象とすると良いんだけど、実コードに落とすとなると結構難しい。

 

posted @ 12:51 | Feedback (0)

Rx Live!

リアクティブフレームワークがリリースされましたね。

Reactive Framework available from DevLabs - Scott Weinstein on .Net, Linq, PowerShell, WPF, and WCF

さっそく実戦投入を決意した。

 

posted @ 11:47 | Feedback (0)

2009年11月16日 #

Quick Hack: SketchFlow for ASP.NET MVCの様なもの

タイトル通りにちょっとQuick Hackしてみた。

SketchFlow ってのは 画面遷移もカンタン設計、MSが新ツールをデモ - @IT とかにざっくりした概要はあるが要するに画面フローをWPF / SilverLight プロジェクトのプロトタイピングとして簡単にできるツールなわけで Expression Blend 3 についてる新機能です。

んで、画面フローをプロトタイピングして、その結果として「これでこの機能できるよねー」とかの検証をしたうえで実装に入るという開発プロセスを踏むわけです。

んで、検証された画面フローの使い道を WPF / SilverLight に限定してしまうのはモッタイナイというわけでやってみたちゅーこと。

手順は簡単で最初にとにかく適当に画面フローを SketchFlowで書いてみる。それをBlendからビルドして試しに動かしてみる。この辺入念に。

納得行く動きをする画面フローができたらおもむろに Blend を終了して、 Sketch.Flow ファイルを Visual Studio で開く。

なんてこたーないXMLが出てくるので XML メニューからスキーマを生成させて、xsd を保存。

XSDをLiquid XML Studioで読み込んで重要になりそうな要素を選んで右クリックでRefactor -> Convert to Global Type で型を割りつける。

自分は Data/Screens 配下の Screen と Data/Connections配下のConnectionが重要とみて実行。(QuickHackなので名前なんて気にしない、デフォルトで出た ScreenType、ConnectionType を名前としてそのまま使った)

XMLを見てみると Screen のTypeがNavigationな物が画面なんで、その画面に対してテンプレートを作る。

$@query
  from ScreenType s in input.Screens where s.Type=="Navigation" select new { filename=s.ClassName+".aspx" , item=s }
@$
$@itemType ScreenType
<html>
<head>
<title>$=item.DisplayName$</title>
</head>
<body>
<h1>$=item.DisplayName$</h1>
<p>リンク</p>
$@list( ConnectionType conn in from c in input.Connections where c.Type=="Navigation" && c.Source==item.ClassName select c )$
<% = Html.ActionLink("$= input.Screens.Single( s=>s.ClassName==conn.Target ).DisplayName$", "$=conn.Target$") %>
$ @$
<p>埋め込み</p>
$@list( ConnectionType conn in from c in input.Connections where c.Type=="Composition" && c.Source==item.ClassName select c )$
<% Html.RenderPartial("$= input.Screens.Single( s=>s.ClassName==conn.Target ).ClassName$"); %><br/>
$ @$

</body>
</html>

こいつは csxっていう自作のコードジェネレータジェネレータの為のテンプレート。要するに画面のクラス名に従って aspx を吐きだしてくれる。内部にはConnections配下にあるConnectionを元にNavigationをリンクとしてまとめて出してくれて、Composition として ascx への RenderPartial を吐く。

同様に Composition を

$@query
  from ScreenType s in input.Screens where s.Type=="Navigation" select new { filename=s.ClassName+".aspx" , item=s }
@$
$@itemType ScreenType
<html>
<head>
<title>$=item.DisplayName$</title>
</head>
<body>
<h1>$=item.DisplayName$</h1>
<p>リンク</p>
$@list( ConnectionType conn in from c in input.Connections where c.Type=="Navigation" && c.Source==item.ClassName select c )$
<% = Html.ActionLink("$= input.Screens.Single( s=>s.ClassName==conn.Target ).DisplayName$", "$=conn.Target$") %>
$ @$
<p>埋め込み</p>
$@list( ConnectionType conn in from c in input.Connections where c.Type=="Composition" && c.Source==item.ClassName select c )$
<% Html.RenderPartial("$= input.Screens.Single( s=>s.ClassName==conn.Target ).ClassName$"); %><br/>
$ @$

</body>
</html>

ってやり(それぞれ適当にtemplateを拡張子として保存)

>"c:\Program Files\csx\csx.exe" -xsd:Sketch.xsd -generateExe -schemaRoot:Data *.template

でコードジェネレータを作って

>Sketch.exe Sketch.Flow

で実行。

めでたくプロトタイプ通りの aspx と ascx が得られましたとさ。

xaml を解釈してもっと細かいデータ取ればより細かい生成制御はできると思う&人手でのデザイン(HTMLコーディング)と機械生成する部分の調整をしないと駄目なんだろうけど、Quick Hack としてはまず成功。

というわけで、斜め上に間違えたSketchFlowの使い方でした。

Let's Enjoy Generate Code!

posted @ 11:46 | Feedback (1)

2009年11月6日 #

新人採用…終了?

というわけで、また社員募集しています での採用も大方終了しました。

いつもの事で Joel テストに従って書き問題をやったので問題を晒してみます。

CSVの生成

 

 以下の仕様に基づいてCSV出力を行うコードを実装して下さい。(記述言語は問いません)

 

・文字列と数値からなるデータを出力する

・各レコードは改行で区切られる(以下例外あり)

・文字列はダブルクォート(")で囲まれる

・文字列内で2つの連続するダブルクォートを一つのダブルクォートのエスケープとして扱う

・文字列内での改行が許される

 

public struct Field {

    public bool IsString; // 要素が string の場合には true

    public string StringValue;

    public int IntegerValue;

}

 

において、以下のメソッド形式で実装して下さい。

 

public static void WriteCsv( string pathName, IEnumerable< Field[] > records )

こんな感じ。

P.S.

BCL4.0ではストリームがIEnumerable食えるようになるらしいねー

だからと言って

using( var sw = new StreamWriter( pathName ) ) sw.WriteLines( from rec in records let recString = (from field in rec select field.IsString ? "\""+ field.StringValue.Replace("\"","\"\"") +"\"" : field.IntegerValue.ToString() ) ).Join(",") select recString );

とかワンライナーやる奴は某誰かと同類という事でご遠慮いただけると幸いです。

posted @ 16:21 | Feedback (2)

2009年10月27日 #

今度はT16までだ

Func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult) Delegate (System)

きたーーー!

public static Expression<Func<TResult>> AsExpression( Expression<Func<TResult>> expression ) { return expression }
public static Expression<Func<T1,TResult>> AsExpression( Expression<Func<T1,TResult>> expression ) { return expression }
… Func のT4まであるパターンに従って作る

Where に埋め込む為に Expression Tree をさらにこねる

なんて事をやっていたみなさん、お仕事4倍のお時間がやってまいりました。

通常の3倍で消化をお願いします。(ぉぃ

この辺、なんとかならんもんなんでしょうか。

posted @ 13:46 | Feedback (0)

2009年10月23日 #

これはやりすぎだろ

VS 2010 Code Intellisense Improvements (VS 2010 and .NET 4.0 Series) - ScottGu's Blog より

Pascal Case Intellisense

The .NET Framework naming guidelines specify that type and member names should be “Pascal Cased” by default.  This means that each word in a type or member should start with a capitalized letter (for example: PageIndexChanged). 

おいおい、これはいくらなんでもやりすぎだろー。

ANE (ArgumentNullException)とか、 SCEA(SelectionChangedEventArgs) とか、AOORE (ArgumentOutOfRangeException)とか。

超絶略語の飛び交うさまが目に浮かぶ。

posted @ 17:06 | Feedback (0)

2009年10月21日 #

VS2010 Beta 2 Live

すでに青柳先生がまとめてくれているのでエディションラインナップを眺めながら時期開発環境にどのエディションを使うか考えてみる。

ちなみにうちの会社の現状は VS2008Pro + OnTime + SubVersion + ReSharper + CC.NET と VS2005+VSS の環境混在で SubVersion のVS AddIn はせずに亀利用という形

さて、VS2010 からは TFS が全エディションに乗っかった形になり基本機能化が大きく OnTime + SubVersion + CC.NET の機能をまとめて TFS に投げられる形になる感。こっそりとVisual SourceSafe 終了のお知らせが流れていないか探してみたけど見当たらず。流れていれば VSS は即刻廃止して SubVersion or TFS 移行のアクションをとらなきゃと思ってるんだけどどうなんでしょう。

Diagnostics の Code Coverage / Profiling により Premium が VSTS 相当系ラインである事を物語ってる感があるが、Database Development が Professional に無いのがアレ。VSTS-DB 相当の機能だけを言ってるのか VS 2008 Pro でも存在していたデータベースプロジェクトやSQL Debugger含めて完全に無いのかによってはおのずと選択肢は決まっちゃいそう。

VS2008からのビルドターゲットのフレームワークバージョンを切り替える機能は継承されるので VS2010を使って過去の.NET Frameworkをターゲットとしての開発は可能なので。( .NET 4 は CLR が変わるのでVS2010のターゲットフレームワーク切り替えはランタイムの切り替えまで含む形で VS2008より強化されている)

Premium からは Expression Studio 3 等のデザイン系およびOffice等のライセンスが付属ってのも大きい、Pro - Premium の価格差次第では実質価格としてPremiumが一番安い形にもなりかねないけどまだ価格が出てきてないんでちょっと躊躇。

 

Windows 7 サポート 充実

私的観測網的には Beta 2 の痛IDE化はまだ実現されていない模様。(時間の問題でしょう)

posted @ 9:56 | Feedback (1)

2009年10月15日 #

IIS7 SEO Toolkit

http://www.iis.net/extensions/SEOToolkit 

SEOとかあんまりしてなかったんだけど、作った物に動かしてみた。

多くのエラーに折れそうになったけどデッドリンクのチェックとか比較的便利。

で、定期的に実行したいんだけどコマンドラインとか、タスクスケジューラーとかで実行ってできるよね、できるよね、できるよね…

どうやったらできるんだよー

(ちなみにSEOって書くと絶対にSPAMコメントが襲来するのは普通です。)

posted @ 15:58 | Feedback (0)

2009年10月7日 #

また社員募集しています

ども、採用広報担当です。(そんな記事ばっかりなので orz)

株式会社クロスワープ/システムエンジニアの転職情報

 

また社員募集やってますってことで、お知らせいたします。

今回の募集ではやる気重視で若い人を募集って事で、腕に覚えのある人だったり、腕に覚えのない人だったりしても募集です。

前者はさておき、後者がここを見てるのか激しく疑問だったりしますけどね。

とりあえず、募集ページでの応募資格どおりですが、プログラマ or else Webデザイナ的な経験のある人で2~3年の実務経験って事で激しくハードル低い設定になっております。

応募資格の枠が広い分、当然に応募者は玉石混交な状態にすでになってます。なので応募時には自己PR等十分に吟味して書いてくれないと書類選考も通れないので注意してくださいね。

 

P.S.

takepara さんのファンは明記してね

posted @ 19:12 | Feedback (5)

2009年9月16日 #

何でもいいから全部書け

「いいから黙ってコメント書け」という話 - miauの避難所

9割賛成

自分は何でもいいから全部書け、コードで表現できてるならそれでよし、コードで表現できてないならコメントでもなんでもいいから書け

コードで十分表現できている事に無駄にコメントで説明をする「必要はない」。

うだうだとコメントで繰り返し説明されるのは「うっとおしい」、コードとコメント両方読むのは「面倒臭い」ので必要以上にコメントをするのはアレだけど、嘘コメントとか実害がなければコメントするのは基本は良い事と思ってます。

あえて言うと「コメントは定型化するな」、

// 初期化
初期化してる
// 処理
try {
    処理してる
}
finally {
    // 後始末
    後始末してる
}

こんなコメントには意味はない。しかし、「決まりだから」で書くとか、お約束なので「頭を使わずに書けるから書いときました」なコメントは無い方がマシ

そういう意味で「いいから黙ってコメント書け」は「コメントなんて書くな」とどっこいどっこいに言いすぎ感あり。コードだろうがコメントだろうがなんだろうが「ちゃんと考えて書け」

このコメントは考えてないっぽいな、このコードは考えてないっぽいな、このドキュメントは考えてないっぽいな。どれも同様に駄目だと思うのであります。

 

自分はコードには結構コメント少ないような気がしますが、interface の周辺はコメントだらけになる傾向があり、そこにコードという表現ができないからコメントをベタベタと書く。interface の定義にコメントでこういう実装をしろよと丸々コードをコメントで書いとくとかもあるし。

 

自分ローカルな問題かもしれんけど、コメントの最大の問題は「コメントを書く為に考えた事によってやったつもりになってコードを書くのを忘れる事が多い」って事。(ぉぃ

 

posted @ 12:34 | Feedback (0)

2009年9月15日 #

ぉぃぉぃ、おいたしちゃダメよ

誰だか解らんけどリファラみて笑った。LINQ の例として LINQ でリバーシを示すのはヨシナサイ。

あれは決して普通ではないと思うよ。

posted @ 19:06 | Feedback (0)

2009年8月18日 #

.NET Reactive Framework を Enjoy! してみた

最初にする事

http://themechanicalbride.blogspot.com/2009/07/introducing-rx-linq-to-events.html の下のほうにURLが出てるんだが

http://evain.net/blog/articles/2009/07/30/rebasing-system-reactive-to-the-net-clr を良く読んで Silverlight 用のランタイム向けになっている System.Reactive.dll にパッチをして通常のCLRで使えるようにする。

    class SalesEventer
    {
        public event EventHandler<SalesEventArgs> Selled;

        public void RaiseSelled( string name, int amount )
        {
            Selled( this, new SalesEventArgs() {name = name, amount = amount});
        }
    }

    internal class SalesEventArgs : EventArgs
    {
        public string name;
        public int amount;
    }

なんかが売れたら売れたよイベントを上げてくれるSalesEventerをこう作った。

    class Program
    {
        static void Main(string[] args)
        {
            SalesEventer eventer = new SalesEventer();

            using (Observable.FromEvent<SalesEventArgs>(eventer, "Selled")
                .Where(
                    salesEvent =>
                    salesEvent.EventArgs.name == "ピザ" && salesEvent.EventArgs.amount >= 3)
                .Subscribe( s => Console.WriteLine("ピザ {0}枚とか食いすぎだろ", s.EventArgs.amount)))
            {
                foreach (int i in Enumerable.Range(1, 5))
                {
                    eventer.RaiseSelled("ピザ", i);
                }
            }
            
            Console.ReadLine();
        }
    }

素晴らしい、イベントをフィルタリングしながらハンドルするのがこんなに簡単にだ。

んで、フィルタリングするだけなら from ... select のLINQクエリ式も書けるので (from ... select ... ).Subscribe( Action ) で書いても良い。

ちなみに using で使ってるのはSubscribeで IDisposable が帰ってくるから、そして Disposeするとイベントのハンドリングは解除されるって事で IDisposable をちゃんと扱えばイベントのハンドリング制御も全く簡単だ。

System.Linq.Observable にはなんだか色々と面白そうな物が転がってるのでみんなも試してみてね!

posted @ 20:25 | Feedback (3)

.NET Reactive Framework

System.Reactive.dll がかなりよさげだ。

http://silverlight.codeplex.com/SourceControl/ListDownloadableCommits.aspx より Latest Version をダウンロードする。

Silverlight3/Source/Binaries 配下に System.Reactive.dll があるからぜひ叩いてみよう。

叩き方は

http://themechanicalbride.blogspot.com/2009/07/introducing-rx-linq-to-events.html

を参考の事。

Enjoy!

posted @ 17:12 | Feedback (0)

2009年7月26日 #

構造体型に対するユーザー定義デフォルトコンストラクタ

ネタ元: ユーザー定義のデフォルトコンストラクタと配列の初期化 - NyaRuRuの日記

.NET アセンブリの仕様としては,構造体にユーザー定義のデフォルトコンストラクタを定義することは可能.ただ,仮に定義したとしても配列の初期化では呼び出されないという点だけで十分に罠過ぎる.

んー、もうちょっと段階分けて書かないと語弊があるかも知れませんですよ。

CTS(Common Type System)のメタデータ構造的にはどんな型にでもデフォルトコンストラクタを定義する事は可能(パラメーターの無い ctor を書く事ができるという意味で)

CLI (CLR)的には値型のデフォルトコンストラクタは呼ばれない。

なんで値型のデフォルトコンストラクタが呼ばれないかというと、呼ぶコードが無いからの一言で済んでしまうんだけど、ILをみてみれば話は速かったりする。

IL でフィールドを定義する時やローカル変数を定義する時には単純にエリアの確保宣言だけなわけでそこにはデフォルトコンストラクタを呼ぶコードもなければ、参照型では生成時に実行する newObj 的な何らかのオペコードが絡んでいるわけでもない。

要するに呼べって言われてないから呼ばれない。

だから値型にデフォルトコンストラクタを定義する意味がない。

結果としてどんなに頑張ってもコンパイラで暗黙に ctor を呼ぶコードを生成するかあきらめて0初期化を受け入れるかの二択

んで

ちなみに値型配列について,ユーザー定義のデフォルトコンストラクタを適用したい場合は,Array.Initialize という専用メソッドを呼び出してやるか,勝手にこのメソッド呼び出しを挿入してくれる言語とコンパイラが必要.

これもまた語弊があって、こういう言語とコンパイラを作ったとするとILレベルでの相互運用性に非常に難のある罠言語ができる

なぜなら、CLI上の言語の殆どはここまでの話しの通りに CLI 仕様そのままをユーザーに受け入れさせるべく言語仕様を設定して実装されている。デフォルトコンストラクタを自動的に呼んでくれるコンパイラのはきだしたアセンブリに含まれる値型をインスタンス化する場合にもそのように動く。

結果的にそんな言語を作ってデフォルトコンストラクタを頑張って定義しても他言語と相互運用する場合に破綻する可能性が非常に高く、そういう言語とコンパイラ+相互運用を実現するために既存の.NET言語とのすり合わせをやる何らかのランタイムが必要。要するに ValueTypeのデフォルトコンストラクタでの初期化を実現する VIR (Value type Initialization support Runtime)をDLRみたいに必死こいて実装しないと駄目で、言語とコンパイラを作ってもその中でクローズドな解にしかなりません。

 

んで、ILレベルで値型に対してエリア宣言のみでなく何らかのイニシャライズ動作を行う為のop Codeを割り当てなかった理由は知らない、「MSの中の人に聞いてくれ」だけど、仕様文書ではパフォーマンスの為となってるし多分パフォーマンスを強く意識しての事だが本質的にはプリミティブオペレーションが複雑になりすぎるからだと思います。

NyaRuRuさんも言及してるけど、配列の初期化を代表格として値型変数がレジスタ割り当てされた時どうすんのよ(一旦メモリにおいてデフォルトコンストラクタ呼んでからレジスタに引き戻す?)とか、ローカル変数にパラメータをそのまま入れる時にローカル変数のデフォルトコンストラクタは呼ぶべき?とか。ローカル変数、フィールド、演算中のテンポラリワーク、色々あるけどすべてにわたって整合性のあるルールとして妥当だったのが「デフォルトの0初期化以外はコード上で明示的に呼ばない限りには行わない」って事かと。

 

posted @ 13:07 | Feedback (0)

2009年7月24日 #

日本語版は機械翻訳されたコンテンツとなります。

ネタ元: 渋木宏明(ひどり) Tumblr - MSDNマガジンが機械翻訳だと!MSは爆発しろ!!

うぎゃーーーーー

http://msdn.microsoft.com/ja-jp/magazine/default.aspx

ご案内: 今後、マガジンの日本語版は機械翻訳されたコンテンツとなります。新しい機械翻訳テクノロジの急速な進歩によって、マイクロソフトのコンテンツを世界のより多くの言語で、より早くお伝えできるようになります。機械翻訳されたコンテンツに対する評価やコメントなど、皆様のフィードバックをお寄せください。

機械翻訳にするぐらいならもう MSDN マガジンなんてやめてしまえ、機械翻訳の読みづらい間違いだらけの日本語読むより原文の方が読みやすいんじゃーーーーー。

 

posted @ 18:37 | Feedback (1)

Where に埋め込む為に Expression Tree をさらにこねる

LINQ to SQL ではパラメータークエリを作るための CompiledQuery クラスがある。

こいつを使うと LINQ to SQL が SQL 文に変換した結果をキャッシュできるし、パラメータ化したクエリを実行できるのでSQLサーバーサイドでのプランキャッシュも有効に働くため、LINQ to SQLで性能を出す為には利用必須と言いたい機能である。

この子を使うと言語上で面白い効果が表れる。

var q = (HogeDataContext db) => from db.table where ... select ... ;

通常は上記はコンパイルできない。 ラムダを var に突っ込むなと怒られる。

var q = CompiledQuery.Compile(  (HogeDataContext db )=>from db.table where ... select ... );

これはコンパイルが通る。なぜなら var は CompiledQuery.Compile の戻り値で示されるFunc型だと決まるし、CompiledQuery.Compile の引数によってラムダは式木である事が強制されてあいまいさが消えるのである。

さて、このようにメソッド引数を介してあいまい性を消す事ができるという知識とそれをもとにちょっとしたユーティリティメソッドを作らないと匿名型を使ったパラメータクエリの元の式木をいじるのは厄介な問題を抱える事になる。

public static Expression<Func<TResult>> AsExpression( Expression<Func<TResult>> expression ) { return expression }
public static Expression<Func<T1,TResult>> AsExpression( Expression<Func<T1,TResult>> expression ) { return expression }
… Func のT4まであるパターンに従って作る

って感じで渡されたそのものをそのまま返す何もしないメソッドを作ると式木を var で持つのが簡単にできる。

var q = AsExpression( (HogeDataContext db) => from db.table where ... select ... );

AsExpression を作った事により引数型で式木である事が強制されるし、戻り値で式木である事が強制されるのでエラーにならずに式木を匿名型に突っ込む事ができるようになったわけだ。(これができないと匿名型を返す LINQ to SQLクエリの式木をいじるなんて事は不可能だ)

 

んで、 where 部を可変にした LINQ to SQL クエリを作るにはもうひとつの段階が必要で、from …での LINQ シンタックスから一旦離れる必要がある。なぜなら、式木として組み立てた predicate を呼ぶ方法が無くなってしまうから。

var pred = AsExpression( (ItemType item) => item.value!=null );

として from i in db.Item where pred(i) select i は全くもってコンパイルが通らない。predは変数なのにメソッドの様に使ったと怒られる。

当然に predは確かに式木なんでそのままでは呼べない。

これをメソッドチェインで書くとどうなるか。

var items = db.Item.Where( pred );

めでたく items にクエリを格納できるはずだ。

そして冒頭に出てきた CompiledQuery の問題が再燃する。

var items = AsExpression( ()=> db.Item.Where(pred) );
Console.WriteLine( items );

とやると

() => value(ConsoleApplication4.Program+<>c__DisplayClass0).Item.Where(value(ConsoleApplication4.Program+<>c__DisplayClass0).pred)

とか言う表示になるはずだ(仔細は貴方の環境次第だが、value(…).predとなっているはずだ)、これをCompiledQueryにしようとしても駄目だ、式木は pred を実行時に参照解決して動作するという事になっているのだ。

もちろん LINQ to SQLで作られたSQL文 がSQL Server上で実行されるときに pred がどうなってるかなんて見に来る事はできないしうまくいかない。

この pred への参照にpred自体を埋め込まなければLINQ to SQL で実行する事はできないしCompiledQueryで使うこともできない。

    public class EmbedLambdaValueVisitor : ExpressionVisitor
    {
        private LambdaExpression _embedLambda;

        public EmbedLambdaValueVisitor(LambdaExpression embedLambda)
        {
            _embedLambda = embedLambda;
        }

        public Expression EmbedLambda( Expression expression )
        {
            return base.Visit(expression);
        }

        protected override Expression VisitMemberAccess(MemberExpression m)
        {
            var lambda = Expression.Lambda(m);
            var func = lambda.Compile();
            if (func.Method.ReturnType == _embedLambda.GetType())
            {
                var value = func.DynamicInvoke();
                if (value == _embedLambda)
                {
                    return _embedLambda;
                }
            }
            return base.VisitMemberAccess(m);
        }
    }

上記の ExpressionVisitor 派生はこの問題を解決する。

EmbedLambdaValueVisitor embed = new EmbedLambdaValueVisitor( pred );
Expression q2 = embed.EmbedLambda(items);
Console.WriteLine( q2 );

とするとこうなっているはずだ

() => value(ConsoleApplication4.Program+<>c__DisplayClass0).Item.Where( (item)=> item.value!=null );

めでたく pred を参照しているところに pred の内容を埋め込む事ができた。

VisitMemberAccessの動作が肝だけど、単純に MemberExpression を LambdaのBodyとして作りコンパイル実行して MemberExpressionの結果を取っている(型チェックなどはショートカットに過ぎない)。それが置き換え対象として指示されたLambdaと一致したら MemberExpression の代わりに Lambda を返す事で式木中のMemberExpressionがラムダの実体に置き換えられるという流れだ。

これを必要なだけ繰り返して MemberExpression をうまくどけてあげれば式木として作ったクエリがLINQ to SQLを介して実行できるようになるだろう。

 

ちなみに from ... select 形式からメソッドチェインの形式への変換は LINQPad でできるよ!って事は某元俺がルールな人から教わった。

posted @ 14:28 | Feedback (3)

2009年7月23日 #

Expression Treeのこね方

MSDN のこの辺、以前と比べて結構充実してたのね。

というわけでExpressionVisitor をコピペして式ツリーをこねてみたぜ。

    public class ReplaceParameterVisitor : ExpressionVisitor
    {
        private ParameterExpression _from;
        private ParameterExpression _to;

        public ReplaceParameterVisitor(ParameterExpression from, ParameterExpression to)
        {
            _from = from;
            _to = to;
        }

        public Expression ChangeParameter(Expression exp)
        {
            return base.Visit(exp);
        }

        protected override Expression VisitParameter(ParameterExpression p)
        {
            if (p == _from) return base.VisitParameter(_to);
            return base.VisitParameter(p);
        }
    }

を作って

        public static Expression<Func<T, bool>> OrElse<T>(Expression<Func<T, bool>> func1, Expression<Func<T, bool>> func2)
        {
            if (func1 == null && func2 == null) throw new ArgumentNullException();
            if (func1 == null) return func2;
            if (func2 == null) return func1;
            var replaceParameterVisitor = new ReplaceParameterVisitor(func2.Parameters[0], func1.Parameters[0]);
            return Expression.Lambda<Func<T, bool>>(
                Expression.OrElse(
                    func1.Body,
                    replaceParameterVisitor.ChangeParameter(func2.Body)
                    ),
                func1.Parameters
            );
        }

として

        static void Main(string[] args)
        {
            Expression<Func<int, bool>> IsTriple = x => x%3 == 0;
            Expression<Func<int, bool>> Contains3 = x => x.ToString().Contains("3");

            Expression<Func<int, bool>> pred = OrElse(IsTriple, Contains3);
            Console.WriteLine(pred.ToString());
            var compiledPred = pred.Compile();
            for (int i = 1; i<=40; i++)
            {
                if (compiledPred(i)) Console.WriteLine("あほ");
                else Console.WriteLine(i);
            }
            Console.WriteLine("おもろー");
            Console.ReadLine();
        }

なんていまさらなネタでテストすると

x => (((x % 3) = 0) || x.ToString().Contains("3"))
1
2
あほ
4
5
あほ
7
8
あほ
10
11
あほ
あほ
14
あほ
16
17
あほ
19
20
あほ
22
あほ
あほ
25
26
あほ
28
29
あほ
あほ
あほ
あほ
あほ
あほ
あほ
あほ
あほ
あほ
40
おもろー

こうなるって事だな。

 

解りやすく言うと複数の Predicate 型ラムダを OrElse で一つの式に結合する為に、func2 のパラメータを参照しているところを func1 のパラメータを参照する様に置き換えてるわけだ。

これで OrElse と AndOlso (似た手順で作れる)を使って複数の Predicate を一個の Predicate に練成できるので LINQ to SQL の Where 句を組み立てるとかが望みのままにできるようになったっていう事ですな。

ExpressionVisitor は標準で BCL のどっかに入れて置いてほしかったぜ!

posted @ 16:29 | Feedback (0)

2009年7月19日 #

匿名型と Lazy Execution

Webアプリケーションの性能なんか9割はキャッシュで決まりますって事でキャッシュモンスターを目指してLazy Executionを頑張ると結構厄介なのが匿名型の存在。

var q = ( from ... select );
foreach( var item in q )
{ 好きにしたまえ }

をキャッシュしようとして System.Web.Caching.Cache を使うには q の実行結果がキャッシュにあればキャッシュから取って、そうでなければ実行して結果を取る事になる。

Cache にヘルパーを作ることで TResult RetriveOrExecute<TResult>( this Cache cache, string key, Func<TResult> filler ) をこんな感じで実装する。

using System.Web.Caching;
static class CacheExtender
{
    public TResult RetriveOrExecute<TResult>(this Cache cache, string key, Func<TResult> filler )
    {
        var result = default( TResult );
        object cacheValue = cache.Get( key );
        if( cacheValue !=null ) return (TResult) cacheValue;
        result = filler();
        cache.Insert(key, result );
        return result;
    }
}

これを TArg の 0 ~4個番作ると Lazy Execution をする系はこうなる。

var q = () => (from ... select );
foreach( var item in Cache.RetriveOrExecute( key, q ) )
{ 好きにしたまえ }

しかし RetriveOrExecute はキャッシュ条件(キーをどうすんのとか、保持期間や依存関係)によってぐちゃぐちゃにパターンが広がるので実際はそんなに甘くない。

キャッシュ条件は呼び出し元しか知らんのだし、細かい設定を RetriveOrExecuteにパラメータオーバーロードでいくらでも増やせるしパラメータに従ってキャッシュ条件を指定できるけど呼び出し元でパラメータの指定の為のコードが増える事は避けがたい。

単純なLazy Executionは容易に作れるけど、実用的なLazy Executionは結構難しいって事。

このパターンならこうってのを Configuration にするのが比較的思いつきやすいし、出力キャッシュでのCache Profileみたいに「コードに入ってくる前の段に設定渡してこれでお願いします」しか選択肢が無いならそれしかない場合には Configuration に頼らざるを得ないけど、基本としては Configuration = External Context なんで避けたいところ。

var q = () => (from ... select );
var cacheConfiguration = Settings.CacheConfigurations["contents"];
foreach( var item in Cache.RetriveOrExecute( key, q ,cacheConfiguration ) )
{ 好きにしたまえ }

なコードをちゃんと読み解くには設定を読まなきゃいけないしうまく動かないときに設定を取り寄せなきゃいけない。 Extenal Context are Evil !

そういったキャッシュ条件回りでごちゃごちゃするんで結局コードジェネレータぶんまわしのパターンに走ってしまったがそうするってぇと今度は匿名型がローブローの様にじわじわ効いてくる。

var で突っ込みたい物が Cache の Get の戻り値のオブジェクトまたは実行結果だったりするので var result = (TResult) Cache.Get() と var result = q() を同じスコープで同じ変数相手に書かなきゃいけないという事。

んで、たどりついたのが Func<TResult>,Func<TArg,TResult>, Func<TArg0,TArg1,TResult>  に対する拡張メソッド DefaultResult

var q = () => (from ... select );
var qResult = q.DefaultResult();

と書いて qResult に q の戻り値の型で null がもらえるってしとくとキャッシュ取得処理はこうなる

qResult = (qの戻り値型)Cache.Get( key );
if( qResult==null ) {
    qResult = q();
    Cache.Insert( qResult );
}

qの戻り値型に object val をキャストする拡張メソッド CastToResult( this Func,object val ) を書くと qの戻り値型をコードの表記上で表記する必要が無くなり

qResult = q.CastToResult( Cache.Get( key ) );

でいけるようになる。

レシピ的には以下の通り

public static class FuncExtender {
    public static TResult DefaultResult<TResult>( this Func<TResult> func ) { return default(TResult); }
    /* TArg の個数分パラメータオーバーライドする */
    public static TResult CastToResult<TResult>( this Func<TResult> func, object val ) { return (TResult) val; }
    /* TArg の個数分パラメータオーバーライドする */
}

型?コンパイラーが意識してる型に合わさるよ、コンパイラーが意識する型と食い違った思い込みで書いてるとおかしくなるけど、それは使う側のコードの問題さ。

posted @ 15:28 | Feedback (0)

2009年7月14日 #

Microsoft XNA Game Studio 3.1

出てました。

ダウンロードの詳細 : Microsoft XNA Game Studio 3.1

インストールした(なぜか会社のPCに)

とりあえずゲームパッドのボタンでビルドサーバになんかするとかその辺から取りかかろうか(ちがうだろ

posted @ 17:34 | Feedback (0)