ログ吐き骨組み

Microsoft Silverlight を入手

デモ大事。Subscribeの時にConsole.WriteLine並べて、実行結果想像つきますよね、というのがいまひとつすぎたので、出力が見える骨組みを作りました。今後のReactive Frameworkの紹介時にソースコード上のDebug.WriteLineは、こーいうことなんですねー、と思ってください。毎回これ乗っけてると長ったらしいので、暗黙の、ということで。

<UserControl x:Class="SilverlightApplication4.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="200" />
        </Grid.ColumnDefinitions>

        <StackPanel Grid.Column="0">
            <Button Name="ExecuteButton" Content="Execute" />
            <Button Name="ErrorButton" Content="Error" />
            <Button Name="ObservableButton" Content="Observable" />
            <Button Name="EnumerableButton" Content="Enumerable" />
        </StackPanel>
        <ScrollViewer Grid.Column="1">
            <TextBlock Name="LogBrowseTextBlock"></TextBlock>
        </ScrollViewer>
    </Grid>
</UserControl>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Windows.Controls.Primitives;
using System.Reflection;

namespace SilverlightApplication4
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            Debug.Set(LogBrowseTextBlock, Dispatcher);

            ExecuteButton.GetClick().Subscribe(() =>
                Observable.Range(1, 10).Subscribe(
                    i => Debug.WriteLine(i),
                    e => Debug.WriteLine(e),
                    () => Debug.WriteLine("completed")
                )
            );

            ErrorButton.GetClick().Subscribe(() =>
                Observable.Range(1, 10)
                    .Do(i => { if (i == 5) throw new Exception(); })
                    .Subscribe(
                        i => Debug.WriteLine(i),
                        e => Debug.WriteLine("onError"),
                        () => Debug.WriteLine("onCompleted")
                    )
            );

            ObservableButton.GetClick().Subscribe(() =>
                GetMethodNames(typeof(Observable)).ToList().ForEach(s => Debug.WriteLine(s))
            );

            EnumerableButton.GetClick().Subscribe(() =>
                  GetMethodNames(typeof(Enumerable)).ToList().ForEach(s => Debug.WriteLine(s))
            );
        }

        IEnumerable<string> GetMethodNames(Type type)
        {
            return type.GetMethods(BindingFlags.Static | BindingFlags.Public)
                .Select(mi => mi.Name)
                .OrderBy(s => s)
                .Distinct();
        }
    }

    public static class Debug
    {
        private static TextBlock textBlock;
        private static Dispatcher dispatcher;

        public static void Set(TextBlock textBlock, Dispatcher dispatcher)
        {
            Debug.textBlock = textBlock;
            Debug.dispatcher = dispatcher;
        }

        public static void WriteLine(object message)
        {
            if (textBlock != null)
            {
                dispatcher.BeginInvoke(() =>
                    textBlock.Text = message.ToString() + Environment.NewLine + textBlock.Text);
            }
        }
    }

    public static class ControlExtensions
    {
        public static IObservable<Event<RoutedEventArgs>> GetClick(this ButtonBase button)
        {
            return Observable.FromEvent<RoutedEventArgs>(button, "Click");
        }
    }
}

幾ら簡易的なものだから、という言い訳がましさを考えても、かなり微妙なコードな気がする。原型はWPFでTraceListenerにTextBlockに書きだすものを追加して、Trace.WriteLineで処理していたもの。SilverlightにはTraceがなかったので、そのまま静的クラス・メソッドに置き換えて、Silverlightにも本来あるDebugを塗り替えちゃうという……。ようするにそのままコピペしても動くよね!的な感じでやりたいな、というところなわけです。ダメ?

ついでにSubscribeの中でObservableって、汚い、ように見えるかも。でも実際これはなんてことなくて、ようはJavaScriptっぽいんですよね、ほんと。DOMContentLoadedにイベント登録のaddEventListener並べるのと一緒で。じゃあ実際こうしてグチャグチャ並べるかというとそうではないようで、実際は拡張メソッドへ記述する、という形で分散していくようですが、まだまだ分からず。Microsoft側の実例やドキュメントが整ってくれないと何とも言えない感じ。

作業環境

画像クリックで原寸サイズ。最近思うところあってVisual Studioの配置をごにょごにょと弄っています。今は、こんな感じに落ち着きました。左にエラー一覧・検索など。右にソリューションエクスプローラー・クラスビュー・スタートページなど。そして左右にそれぞれコードウィンドウを分割。原則的にメインウィンドウは左。コード定義ウィンドウを右ウィンドウに開いて常時表示。もしくはXAML編集と並列したり。といったところです。コード定義ウィンドウはデカい画面で常時表示で初めて効果を発揮しますね、素晴らしく便利。

30インチ 2560x1600の無駄遣いが火を吹く!というわけですが、やっぱ広いって便利、エディタウィンドウ2面同時表示って便利、です。30インチでなくても、横2560は19インチ1280x1024のデュアルで行けます。ただ、実際はこれに加えてデバッグ時のプログラム本体なりブラウザなりを置いておく場所が欲しいので、その場合はデュアルじゃ足りないですね……。グラフィックボードが一枚でトリプルをサポートしてくれれば、というか、するべき、ですよね。ATIのEyefinityにはとても期待してます。

.NET Reactive Framework メソッド探訪第四回:メソッド一覧

前回が少し、明らかに説明不足でした……。Subscribeが原則としてIObserverを受ける(直接ラムダ式でonNextを記述するのはただの省略記法)ということは、IObserverを別に作っておける。もっと進めると、Subscribeで実行されるコード本体は自由に入れ替え可能。ということになります。ここが、イベント直記入に対するReactive Frameworkの強みの一つ。それ○○パターンだって?そうですね?その○○パターンがデフォルトで何も考えず使えるのならば、素晴らしいと思います。

では、ここからようやくメソッド探訪。の前に、全メソッド一覧を見てみます。IObservable<T>拡張メソッド一覧。そう、最初感動したのは、ドット打ってIntelliSenseに並んだこのメソッド名を見て、なのよね。いかにも素晴らしいことが出来そうな予感だったという。

Aggregate
Amb
AsObservable
Catch
CombineLatest
Concat
Cons
Contains
Count
Create
Defer
Delay
Dispatch
Do
Empty
Finally
First
FirstOrDefault
Flatten
ForkJoin
FromEvent
Generate
GetEnumerator
HoldUntilChanged
Interval
Last
LastOrDefault
Latest
Let
LetRec
LongCount
Merge
MostRecent
Never
Next
Post
Range
Reify
Repeat
Retry
Return
Sample
Scan
Select
SelectMany
Send
Single
SingleOrDefault
Skip
SkipWhile
Spawn
Start
Subscribe
Synchronize
Take
TakeWhile
Throttle
Throw
Timeout
ToAsync
ToBehavior
ToEnumerable
ToObservable
ToSubject
Until
WaitUntil
Where
Zip

かなり多い。68個ある。名前から想像つくのもあれば全くつかないものも。順番はどうしようかな、LinqではGenerating, Projection and Filtering, Join, Set, Ordering, Grouping, Aggregate, Paging, Convertで分けられたので、何らかの指針でもって分けた方が良いのは間違いないのですが、実際に中を突っつかないと何が何なのか全く分からない。

Func<Type, IEnumerable<string>> GetMethodNames = 
    type => type
        .GetMethods(BindingFlags.Static|BindingFlags.Public)
        .Select(mi => mi.Name)
        .OrderBy(s => s)
        .Distinct();

var enumerable = GetMethodNames(typeof(Enumerable));
var observable = GetMethodNames(typeof(Observable));

observable.Except(enumerable).ToList().ForEach(Console.WriteLine);

メソッド名一覧はobservableのみを吐いたもので、上のコードはEnumerableとの差分も取ってみたものです。Enumerableと重複しないものは47個でした。そんなに被ってる、というわけではないですね……。ちなみに、Enumerableは50個。覚えきれているので、もっと少ないと思ってたんですが、意外と多かった。Linq to Objectsもきちんと理解して使えるようになるのに半年ぐらいかかってしまっているので、今回も長期戦かなー。ちんたらやってる間にドキュメントが出てくるのを期待したい。

.NET Reactive Framework メソッド探訪第三回:Subscribe

メソッド探訪とか言いながら、ちっとも探訪してません。今回までが基礎知識で、Linq to Objectsで言ったらイテレータがどうこう、という段階。次回以降はメソッドを見ていきたいと思います。では、最後の予習ということで、Subscribe。SubscribeはIObservable<T>連鎖の終点となるもので(但し、別にSubscribeだけが終点というわけではない、FirstとかCountとか、終点になるものは他にもあります)ForEachのようなもの。戻り値はIDisposableで、イベント発生の監視を止めたい時はDisposeを呼ぶ。と、第一回の時に書きましたので、今回は別の方向から見ていきます。

static void Main(string[] args)
{
    Observable.Range(1, 10).Subscribe(Observer.Create(
        (int i) => Console.WriteLine(i), // OnNext
        e => { throw e; }, // OnError
        () => Console.WriteLine("Completed") // OnCompleted
    ));

    Console.ReadLine();
}

今回はコンソールアプリで。結果は想像つくとおり、1から10、最後にCompletedを表示。Observable.RangeはEnumerable.Rangeと同じです。というか中身的にはEnumerable.Range.ToObservableで変換されているだけです。RepeatとEmptyも用意されているので、ちょっとしたメソッド確認用に便利に使えると思います。それにしてもコード、ゴチャゴチャしてますねえ、わけわかんない書き方してわざと難解にやってるんじゃないだろうかって感じですが、その通りです、ので、普通の書き方も下の方でちゃんと書きます。で、SubscribeはIObservable<T>のメソッドとして定義されています。IEnumerable<T>がGetEnumeratorを持つように、IObservableはSubscribeを持つ。そして、原則Subscribeの引数はIObserver<T>です。

public interface IObservable<T>
{
    IDisposable Subscribe(IObserver<T> observer);
}

public interface IObserver<T>
{
    void OnCompleted();
    void OnError(Exception exception);
    void OnNext(T value);
}

インターフェイスなので直接生成することは出来ない。よってファクトリメソッドObserver.Createを用いて生成します。生成されるのは前回見たAnonymousEnumeratorと同じくinternalのジェネリッククラス「AnonymousObserver<T>」で、インターフェイスの各メソッドにデリゲートを直接放り込むだけの単純明快なものです。勿論、IObservable<T>を継承して自前の専用のものを用意しても構いませんが、クロージャを活かせば、自前で定義する意味など少しもありません。コンパイラの自動生成クラス任せでOK。

これの動作はメソッド名通りで、OnNextは値が来るたびに実行されるメソッド。OnCompletedは全て完了した時(FromEvent経由のものなど、実質無限リピート状態の場合は、Disposeが呼ばれた時)に実行されるメソッド。OnErrorは例外が発生した時に実行されるメソッド。発生した例外は原則catchされるので、再スローしたい時は、そのまんまですがthrow eの明示が必要です。

……それにしても面倒くさい。Observer.Createは。onNextだけが書ければいいんだよ!ってシーンにわざわざ空の式を書けとでも?(ちなみに空は()=>{}です) 大体がして、推論出来ないから(int i)だとか、型を書かなければならないのもかったるい。というわけで、拡張メソッドが用意されています。

public static IDisposable Subscribe<TSource>(this IObservable<TSource> source);
public static IDisposable Subscribe<TSource>(this IObservable<TSource> source, Action onNext);
public static IDisposable Subscribe<TSource>(this IObservable<TSource> source, Action<TSource> onNext);
public static IDisposable Subscribe<TSource>(this IObservable<TSource> source, Action onNext, Action<Exception> onError);
public static IDisposable Subscribe<TSource>(this IObservable<TSource> source, Action<TSource> onNext, Action<Exception> onError);
public static IDisposable Subscribe<TSource>(this IObservable<TSource> source, Action onNext, Action<Exception> onError, Action onCompleted);
public static IDisposable Subscribe<TSource>(this IObservable<TSource> source, Action<TSource> onNext, Action<Exception> onError, Action onCompleted);

いっぱいありますけど、ようするに無視した部分は空のメソッドが代わりに埋められる、というだけの話。これを使えば、最初の例は、onNextだけにすると

Observable.Range(1, 10).Subscribe(i => Console.WriteLine(i));

と、簡潔明快に記述出来るわけです。メデタシメデタシ。

過去記事

.NET Reactive Framework メソッド探訪第一回:FromEvent
.NET Reactive Framework メソッド探訪第二回:AnonymousEnumerable

無限リピートの幸福

Reactive Frameworkが、結構に無限リピートな感じなので、関連してC# Linqでどう書くにあった13日金曜日問題を今更書いてみた。n番煎じ。

// 今日から2013年12月31日までの、13日の金曜日とその総数を表示してください。
// 「今日」を無限リピートという方針で書いてみたりして(総数は省略)
// 利点はTodayを変数として外側に定義する必要が無くLinq内に閉じ込められる
// Toを求めるのに足したり引いたりする必要がなく自然に書ける、の二つかしらん
// 「まで」という問いに対してTakeWhileで解答するのは自然で良いと思う

Enumerable.Repeat(DateTime.Now, int.MaxValue)
    .Select((d, i) => d.AddDays(i))
    .TakeWhile(d => d.Year < 2014)
    .Where(d => (d.DayOfWeek == DayOfWeek.Friday) && (d.Day == 13))
    .ToList()
    .ForEach(d => Console.WriteLine(d.ToShortDateString()));

TakeWhileが好きです。問題文に対して、自然に解答出来るような気がするので。「今日から(Repeat)」「2013年12月31日までの(TakeWhile)」「13日の金曜日(Where)」。実に自然に記述できる。いやまあ、Repeatが直感的かというと結構微妙なところではありますが。Rangeでfrom,toのほうが自然だろ常識的に考えて、というのも確かなんですけど、Rangeだとtoを作るのに計算式が必要ってのが、ちょっと違うかな、と。

Pizza (programming language)のexampleにもあるような、Streamを始めとして何かを無限リピートしてTakeWhileで終了条件を設定、というのはパターンとして結構幅広く使える、と思う。ある種のデザインパターン。イディオムイディオム。参考リンクはC# 3.0 と while(true) と Iterator - NyaRuRuの日記この辺り。

例えばVS2010から搭載されるEnumerable.Zipや、あとCycleを定義してみる。

// この二つを混ぜ合わす(VS2010で搭載されるZip関数)
var seq1 = Enumerable.Range(1, 10);
var seq2 = Enumerable.Range(10, 10);
Enumerable.Repeat(new { e1 = seq1.GetEnumerator(), e2 = seq2.GetEnumerator() }, int.MaxValue)
    .TakeWhile(t => t.e1.MoveNext() && t.e2.MoveNext())
    .Select(t => t.e1.Current + t.e2.Current); // ここがZipのSelectorの部分
// foo,bar,hoge,foo,bar,hogeを無限に繰り返す
var elements = new[] { "foo", "bar", "hoge" };
var cycle = Enumerable.Repeat(elements, int.MaxValue).SelectMany(ar => ar);

Linqのお陰でかつてない勢いでint.MaxValueを使っているこの頃。Repeatも万能ですねえ。いやまあ、もう素直にAchiral使えよって話なんですが、標準メソッドのみで粘るのも面白くて。そういえばでついでなのでlinq.jsでもやってみた。

// JavaScriptはAddDaysがないので副作用全開でTodayを
// setHours(24)で翌日にしてしまう、という方針でやってみた

E.Repeat(new Date())
 .Do("$.setHours(24)")
 .TakeWhile("$.getFullYear() < 2014")
 .Where("$.getDay() == 5 && $.getDate() == 13")
 .ForEach("alert($)");

DoはReactive Frameworkにもありました。副作用を加えた上で素通しするメソッド。副作用は嫌なものです。汚いです。何が嫌かというと、動作を考えるのに見る範囲を広げなきゃいかんところかなあ。そしてLinqの何がいいかというと、見る範囲が物凄く限定される(ラムダ式一文だけを見ればいい)と思っている。だからLinq内でクロージャ(というか外部の変数をキャプチャして使う)もあんま好ましくないし、C#クエリ構文のletも好きじゃない。なるべくなら使いたくない。長文耐性なのは分かるけれど、カッコやインデントがなくてスマートだけれど、その分だけスコープが不明瞭になるという側面が否めない。まあ、letが必要なシチュエーションをSelectManyでやると、大抵はもっと奇怪になるのですけど。

んでまあ、この場合だとAddDaysのかわりにnew Date(year,month,day)で新しいのを作れば副作用なくSelectが使えるわけですが、ありきたりで面白くないと思ったので別な方向に走ってみた。というか、無限リピートは、無限リピートする何かに対して副作用全開で操作を加え続ける、という形の方が面白いというか実用的というか普通だとは思う。冒頭の例みたいなやつだと、別にRangeでよくね?って感じですし。Haskellじゃないんだから、潔癖症にならずに、副作用といかに楽しくお付き合いするかが大事なのですかね。

あ、ちなみに$は引数が一つの場合の省略記法です。こういった機能はScalaにもある。引数が一つのみの場合が大半なので、記述がグッと縮まるし、何よりも引数名を付ける必要がないのが嬉しい。C#でも使えるようになると嬉しいなあ、とずっと思ってるんですが中々どうして無理なんですかねえ、残念。

.NET Reactive Framework メソッド探訪第二回:AnonymousEnumerable

予定は常に変更されるもの、というわけで、今回はAnonymousEnumerableとAnonymousEnumeratorを見たいと思います。表に出てこない、internalのクラスな上に、内部でも全然使われていないので、見る必要はあんまりない。のですが、これと対になるAnonymousObservableとAnonymousObserverを見るにあたって、先に慣れ親しんだIEnumerable/Enumeratorで考えたほうが分かりやすかったので、これを先に考えます。

class AnonymousEnumerable<T> : IEnumerable<T>
{
    private Func<IEnumerator<T>> getEnumerator;
    public AnonymousEnumerable(Func<IEnumerator<T>> getEnumerator)
    {
        // 以下略
}

class AnonymousEnumerator<T> : IEnumerator<T>
{
    private Func<T> current;
    private Action dispose;
    private Func<bool> moveNext;

    public AnonymousEnumerator(Func<bool> moveNext, Func<T> current, Action dispose)
    {
        this.moveNext = moveNext;
        this.current = current;
        this.dispose = dispose;
    }

    public T Current
    {
        get { return this.current(); }
    }

    public bool MoveNext()
    {
        return this.moveNext();
    }
    
    // 以下略
}

Reflectorで見ちゃってるので、一部だけ(ライセンス!)。ただ、どれも一行なので見るまでもなく何をやってるのか想像付くと思います。 お馴染みのインターフェイスを実装しているだけですが、その実装を全てコンストラクタで受ける関数に任せています。ようするにこれはどういうことなのかというと、実例としてEnumerable.RepeatとSelectをAnonymousEnumerableで実装してみるとこうなります。

public static class Enumerable
{
    public static IEnumerable<T> Repeat<T>(T element, int count)
    {
        return new AnonymousEnumerable<T>(() =>
        {
            var index = 0;
            var current = default(T);
            return new AnonymousEnumerator<T>(
                () => // MoveNext
                {
                    if (index == 0) current = element;
                    return (index++ < count);
                },
                () => current, // Current
                () => { } // Dispose
            );
        });
    }
    
    // 実際のSelectは<TSource,TResult>ですが、都合により略
    public static IEnumerable<T> Select<T>(this IEnumerable<T> source, Func<T, T> selector)
    {
        return new AnonymousEnumerable<T>(() =>
        {
            var enumerator = source.GetEnumerator();
            return new AnonymousEnumerator<T>(
                () => enumerator.MoveNext(),
                () => selector(enumerator.Current),
                () => enumerator.Dispose()
            );
        });
    }
}

勿論、普通はyieldを使えばいいわけですが、もしyieldがなければ、こういう形で実装するのが簡潔でベスト、に見える。クロージャでコンパイラにクラス生成を任せているわけですねー。外部イテレータですが、外部のクラスに分割せず中に書けるため、すっきり分かりやすい。この発想はあったけどC#でやるという発想はなかったわ、というわけで結構感動しました。

そうそう、この仕組みはlinq.jsと丸っきり同じなのです。感動ってのは、私の基本方針は間違ってなかったんだ!の裏付けでの喜びなので不純です。そのJavaScriptでの実装はこんな感じ。

Linq.Object = function(getEnumerator)
{
    this.GetEnumerator = getEnumerator;
}

Linq.Enumerator = function(moveNext)
{
    this.Current = null;
    this.MoveNext = moveNext;
}

Repeat: function(element, count)
{
    return new Linq.Object(function()
    {
        var index = 0;
        return new Linq.Enumerator(function()
        {
            if (this.Current == null) this.Current = element;
            return (index++ < count);
        });
    });
}

Select: function(selector)
{
    var source = this;
    selector = Linq.Utils.CreateFunctor(selector);

    return new Linq.Object(function()
    {
        var enumerator = source.GetEnumerator();
        var index = 0;

        return new Linq.Enumerator(function()
        {
            if (enumerator.MoveNext())
            {
                this.Current = selector(enumerator.Current, index++);
                return true;
            }
            else
            {
                return false;
            }
        });
    });
}

スコープが若干違うかなって感じですが、大体同じです。(Linq.ObjectはLinq.Enumerableに名前変えよう……)。JavaScriptだからこそ、yieldがなくてもスッキリ定義出来る!と思っていただけに、そっかあ、そういう手を使えば良かったのかと少しショックだったりして。ほんとC#は柔軟な言語で、むしろもうJavaScriptよりもLightWeightだよ!

さて、次回はAnonymousObservableとAnonymousObserverを見てみることにします。勿論、予定は未定です。ていうかSubscribeはどうした、というと、Subscribeの中身はAnonymousObserverなので全然横道にそれてません、大丈夫です、まだ一直線です。

.NET Reactive Framework メソッド探訪第一回:FromEvent

まず、リアクティブフレームワークとは何ぞや、ということなのですが今のところInfoQ: .NETリアクティブフレームワーク(Rx)がLINQ to Eventsを可能にするの記事ぐらいしか情報はありません。.NET 4.0に含まれる(かもしれない)ということ、現在のところSilverlight Toolkitの単体テストのところにこっそりと配置されていること。それだけです。紹介も、記事中にもリンクされていますがunfold: Introducing Rx (Linq to Events)の一連の記事ぐらいしかありません。これの前文が中々に素敵です。

Buried deep in the bin folder of the Silverlight Toolkit Unit Tests is a hidden gem: The Rx Framework (System.Reactive.dll). If you glanced quickly you’d miss it altogether but it’s one of the most exciting additions to the .NET framework since Linq.

今のところ微妙にパッとしない(Parallelは簡単に使えるがゆえにインパクトが足らない)4.0の隠し玉はコレですね、間違いない。軽く触ってみたのですが、中々に感動的。Linq to Objects好きならば間違いなく琴線に触れます。C#3.0がコレクションの操作をforeachからLinqに変えてしまったように、.NET4.0はイベントもLinqに変わる。まさにLinq to Everywhere! Functional Reactive Programming!

How to use

Silverlight ToolkitをダウンロードしてSource/Binaries/System.Reactive.dllを頂けば完了。ただし、これはそのままだとSilverlightのプロジェクトでしか動作しないので、その他ので利用したい場合はここの記事に示されているように、githubに公開されているコードを実行(Cecilのdllが必要、記事文中にリンクされています)して変換する必要があります。今回はとりあえず、Silverlightで試してみたいと思います。こちらはこちらで、Silverlight 3 Toolsのダウンロードが必要ですけれど。

実例

Microsoft Silverlight を入手

マウスの動きに円が追随する、という単純なものをSilverlightで作ってみました。移動は完全に追随するのではなく、座標が15で割り切れる位置の場合のみ移動としました。スナップすることをイメージしたつもりなのですが、動きがガクガクです。これは、マウス移動に完全に追随して全ての座標でイベントが発生するわけではない = 15で割り切れる座標を通過してもイベントが発生しない場合がある = 動きがガクガク。というわけで、スナップしたい場合はWhereで間引くのではなく、Selectで近傍座標に寄せるべきです、が、いやまあ、例なので……。

// XAMLではなく全部コード上に書いたのは両方を張るのが面倒だから……
// 内容はCanvasとEllipseを配置するというもので、本筋とは関係ありません
InitializeComponent();
var canvas = new Canvas { Background = new SolidColorBrush(new Color {A=255, R = 100, G = 100, B = 100 }) };
var ellipse = new Ellipse { Height = 30, Width = 30, Fill = new SolidColorBrush(Colors.Orange) };
canvas.Children.Add(ellipse);
this.Content = canvas;

// FromEventはイベント発火がトリガとなってLinq発動
// 後段に送られるのはEvent<T>というもので、
// SenderとEventArgsという読み取り専用プロパティを持つクラス
var canvasMove = Observable.FromEvent<MouseEventArgs>(canvas, "MouseMove")
    .Select(e => e.EventArgs.GetPosition(canvas))
    .Where(p => (p.X % 15 == 0) || (p.Y % 15 == 0))
    .Subscribe(p =>
    {
        ellipse.SetValue(Canvas.LeftProperty, p.X - ellipse.Width / 2);
        ellipse.SetValue(Canvas.TopProperty, p.Y - ellipse.Height / 2);
    });

// Subscribeの戻り値の型はIDisposable
// Disposeを呼ぶと登録したイベントをデタッチすることが出来る
// デタッチしないなら取得する必要は特にはない
// canvasMove.Dispose();

MouseMoveでイベントが発火する度にLinqを通る。なるほど、イベントがリストに、見える。イベントを無限リスト生成として捉えることで、イベントに対してLinq操作が可能になった。ObserverパターンとIteratorパターンは同じだったんだよ!なんだってー!みたいなノリがある。もう少し丁寧に見ると、Observable.FromEventでイベントをPush型の無限リストに変換。戻り値はIObservable<T>。イベント発火時に後段に流れてくるのはEvent<T>。これは通常のイベント登録時に使うsenderとeventArgsをラップしただけの単純なもの。あとはIObservableに用意されているメソッド(Select, Where, TakeWhileなどお馴染みのものから、Delay, WaitUntilなどイベント用の目新しいメソッドなど多数)を繋げて、最後にSubscribe。このSubscribeは、つまり通常のイベント登録時のメソッド本文の役割を果たす。Linqで言ったらForEachのようなもの。Subscribeのオーバーロードも幾つかあるのですが、それはまた後日。

// つまるところ、以下のコードと同じだったりはする
// ただ、IObservable<T>は通常のイベント登録では無理な複雑な操作が簡単、
// そして何よりも、このような単純なコードでもそんなに複雑になっていない!
canvas.MouseMove += (sender, e) =>
{
    var pos = e.GetPosition(canvas); // Select
    if (!(pos.X % 15 == 0 || pos.Y % 15 == 0)) return; // Where
    ellipse.SetValue(Canvas.LeftProperty, pos.X - ellipse.Width / 2);
    ellipse.SetValue(Canvas.TopProperty, pos.Y - ellipse.Height / 2);
};

通常のイベント登録と対比してみると分かりやすいかしらん。FromEventではMouseEventArgsという型を明示する必要があるのがカッタルイ。推論は偉大。が、しかし、IObservableが複雑な操作が可能なのに対し、イベントに追加では直球なものしか書けない。また、複雑な操作が可能なわりには、FromEventは驚くほどシンプルに書ける。シンプルな操作でも(記述するのに)重たくない、というのは特筆すべきことじゃあないでしょうか。

// stringを避けたこういう登録方法もあるけれど、面倒なうえに警告出る
Observable.FromEvent((EventHandler<MouseEventArgs> h) => new MouseEventHandler(h),
        h => canvas.MouseMove += h, // addHandler
        h => canvas.MouseMove -= h) // removeHandler
    .Subscribe(e => Debug.WriteLine(e.EventArgs.GetPosition(canvas)));

ところで、イベント名をstringで書くのはどうよ、ていうかJavaScriptのaddEventHandlerみたいで嫌だよね?ね?リファクタリング効かないわ、IntelliSenseも動かないわでロクなことがない。というわけで、FromEventのオーバーロードを見ると、ちゃんと普通に登録する方法も用意されてはいる。一応、用意、されては、いる。が、しかし、あんまりだー。あんまりすぎるー。流れてくるEventhandler<MouseEventArgs>をMouseMoveが受け取ってくれないので、第一引数でMouseEventHandlerに変換する(ところで警告が消せないのですが、警告無しで処理する方法ってあるのかしらん)。あとは、addとremoveの登録。長ったらすぎてこれはダメぽ。確かに、こんなんなら、stringでいいです……。

Microsoft Silverlight を入手

// 普段あまり書かないMouseEventArgsとかいう型定義は書きにくいし
// メソッド名もstringで書くのはミスが出がち、ということで
// 拡張メソッドでイベント取り出し用のメソッドを予め作っておくと良い
public static IObservable<Event<MouseEventArgs>> GetMouseMove(this UIElement elem)
{
    return Observable.FromEvent<MouseEventArgs>(elem, "MouseMove");
}

// マウスの軌跡を1秒後に描画します
canvas.GetMouseMove()
    .Select(e => e.EventArgs.GetPosition(canvas))
    .Delay(1000)
    .Subscribe(p =>Dispatcher.BeginInvoke(()=>
    {
        ellipse.SetValue(Canvas.LeftProperty, p.X - ellipse.Width / 2);
        ellipse.SetValue(Canvas.TopProperty, p.Y - ellipse.Height / 2);
    }));

汚い部分は隔離!ということで、拡張メソッドに退避してやると、美しく書ける。いやまあ、この辺は全部unfold: The Joy of Rx: Extension Eventsに書いてあることなのですけど。んで、デモ的にもう少し面白げがあったほうがいいかな、と思ったのでDelayを足してみました。1秒後にマウス移動の軌跡を描画します。グルグルーっとマウス動かして止めてみてください。スムーズ、とは言い難いですね、しょんぼり。記述も、Delayを足すだけ。と言いたかったんですがBeginInvokeかあ、これどーにかなる方法ないかなあ。

次回

全10回ぐらいで、全部のメソッドを紹介するつもりです。私が理解できればの話ですが。ちょこちょこと実例的なものも交えていきたいと思います。私が使いこなせればの話ですが。というわけで、次回はSubscribeのオーバーロードの紹介にしたいと思います。Reactive Frameworkならではの魅力、に関してはもう少し先になってしまいそう。少し飛ばして、非同期連結の話なんかを先に持ってきた方が良いかなあ。

3桁到達

開発者用のOAuth管理ページで認証ユーザー数が見れるのですが(「誰が」までは分からないので安心してください)、いつのまにやらユーザー数が100を超えていました。三桁!奇跡的ですね。はてな同期云々とかフォトライフ云々は壊滅的な状態なので。世の中そんなものです。ついでにこのサイトのアクセス数も壊滅的だったりはします。成り行きでプログラミング系サイトに転換してから半年、以上は経つ感じですが、伸びもせず縮みもせず、ずっと低調をキープ。サイトの内容がガラッと変わったのにアクセス平均が変わらないってのも面白いですけど。検索サイトからのアクセスがほとんどなので、検索キーワードが入れ替わって、でも流入人口は変わらずという。あー、まあ、少し増えた、かな。つまりかわりに常連的な人口が減った、と。おお、虚しい!悲しい!RSSリーダー登録数も伸びないしね。しょぼーん。

ゲーム系サイトへは、XNAで返り咲きしたいとこっそり思ってます。XNAは使用言語がC#だからね。C#好きーなのです、私。積みタスクを全部消化したらXNAやりたいんですが中々どうして……。 そういえばインディーズゲーム(XBIG)を完全スルー状態なのはぶっちけ(略)

fromにクライアント名が出るようになってからは、googleのサイト検索で利用具合が見つかるので、毎日24時間以内の結果を眺めていたりします。見ていて思うというか教訓は、デフォルト設定大事ってことでしょうかね。カスタマイズせずそのまま、カスタマイズする場合も、デフォルトを残しつつ細部を変える、という感じなので、デフォルトの投稿文はちゃんとしたものを用意しなきゃダメなんですね。当たり前といえば当たり前なのですけど、この辺はてなついったー同期ツールは大失敗していて、どうせカスタマイズするだろうと踏んで、書式のサンプルとばかりにゴテゴテのものをデフォルトにしてしまったので……。反省。

あと、デフォルトでは「プレイ中タイトルの状況が変わった時の投稿」はオフにしているのですが、意外とこれをオンにする人が多かったのも驚き。これオンにすると物凄い勢いで投稿されるんですよ。更新間隔を5分にすると、例えばGoW2のHordeすると、WAVE44,45,46...と、全部のWAVE投稿するんじゃないか、最新50個の投稿が全部XboxInfoTwit経由になってますが大丈夫?みたいなことになる。こういう滅茶苦茶なことが出来るのはローカルで動くツールならでは、なのですが(ウェブサービス系じゃあ、ちいと無理ですね、秋のTwitter対応が仮に実績やプレイ状況の投稿に対応するとしても、ここまでの連投は無理かと)フォロワーの目からどうなのか、というと、まあ、分からにゃい。いや、本人の満足が一番だと思いますよ。一日のプレイ後に投稿を眺めると、状況の変化がよく見えて結構楽しかったりはします。お薦めはしませんけどお薦め。別アカでやるなら何も問題なくお薦め。

JavaScriptで要素追加するやり方

ド素人がjQueryとprototype.jsではどう書くのかな、と思っただけです。メジャーな両者ですが実はまともに使ったことがないのです。困ったことに。しょうがないので見よう見まねで書く。

<!-- このselectにoptionを一個追加する -->
    
<select id="selectID">
    <option value="1">hugahuga</option>
</select>

<script type="text/javascript">

    // 素のJavaScriptその1(古臭いというか微妙な……)
    var option = new Option("要素", "属性");
    var select = document.getElementById("selectID");
    select.options[select.options.length] = option;

    // 素のJavaScriptその2(これはダルい)
    var option = document.createElement("option");
    option.setAttribute("value", "属性");
    option.appendChild(document.createTextNode("要素"));
    document.getElementById("selectID").appendChild(option);

    // みんな大好きjQuery
    $("<option>").attr({ value: "属性" }).text("要素").appendTo("#selectID");

    // 何だかんだで好きなprototype.js
    var option = new Element("option", { value: "属性" }).update("要素");
    $("selectID").insert(option);

    // linq.js + linq.xml.jsの関数型構築
    var option = X.Elem("option", X.Attr("value", "属性"), "要素");
    X.ID("selectID").Add(option);
    
</script>

素のJavaScriptその1はねーよ、というわけで、その2をいかにスマートにやるかという話。だと思う。jQueryのappendToが合理的というか便利なのは分かるけど、キモく感じてしまう。んで、どれが好きかっていたら、当然自分で作ってるlinq.jsのが一番好きですよ(笑)

// linq.js + linq.xml.js
var options = E.RangeTo(1, 12).Select(function(i)
{
    return X.Elem("option", X.Attr("value", i), i + "月");
});
X.ID("selectID").Add(options);

// prototype.js
var options = $R(1, 12, false).map(function(i)
{
    return new Element("option", { value: i }).update(i + "月");
});
var elem = $("selectID");
options.each(function(e) { elem.insert(e) });

X.Elem()もAdd()もLinqオブジェクト/可変長配列を受け取れるので、まとめてドバーっと追加が結構楽かな、と思います。eachとかじゃなく、そのまんま追加出来るってのが大事。上の例だと、prototype.jsではmapでoptionsを作らずそのまんまeachでinsertしちゃえばいいぢゃん、というのはそのとーりなんですが(2回もループ回ることになるしね、あ、linq.jsのは遅延評価しているのでループはAddで呼び出される時の1回しか回りません)、配列(的なもの)が既にある状態ってのは、結構ありますよね?

と、何故か突然アピールしてますがlinq.xml.jsは作りかけで放置しているので足りない関数がいっぱいあるんですけどね!

enumの日本語別名とか三項演算子ネストとか

enumのToStringで日本語名になって欲しいというケースはとてもあるある。enum に文字列の属性を: いげ太のブログ2008-01-24 - 当面C#と.NETな記録の記事を見て、今まで拡張メソッドで処理することが多かったのですが、やっぱり見た目は属性のほうがスッキリするなあ、と思った。記述が本体と離れないのが良いですよね。処理速度とか、そんなに頻繁に繰り返し繰り返し呼ぶものでもないしリフレクション上等!それ気にしたらSerializeとか出来ない!とか思ったので、基本は属性で処理することにしてみた。

enum Fruit
{
    [AliasName("ブドウ")]
    Grape,
    [AliasName("リンゴ")]
    Apple,
    [AliasName("オレンジ")]
    Orange
}

static void Main(string[] args)
{
    var fruit = Fruit.Orange;
    Console.WriteLine(fruit.ToAliasName());
}

こんな感じに定義してこんな感じに使う、と。定義もスッキリ、呼び出し時もToString的に拡張メソッドでスッキリ。

[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class AliasNameAttribute : Attribute
{
    public string AliasName { get; private set; }

    public AliasNameAttribute(string aliasName)
    {
        AliasName = aliasName;
    }
}

public static class Ext
{
    // どうしてもメソッドチェインを崩したくない人用
    public static T ThrowIf<T>(this T value, Func<T, bool> predicate, Exception exception)
    {
        if (predicate(value)) throw exception;
        else return value;
    }

    public static string ToAliasName(this Enum value)
    {
        return value.GetType()
            .GetField(value.ToString())
            .GetCustomAttributes(typeof(AliasNameAttribute), false)
            .Cast<AliasNameAttribute>()
            .FirstOrDefault()
            .ThrowIf(a => a == null, new ArgumentException("属性が設定されていません。"))
            .AliasName;
    }
}

ドットが縦にならんでると何だか楽しい。というわけで、チェーンを崩さずに例外を放り投げるための拡張メソッドThrowIfを用意してみた。nullが邪魔!邪魔!nullが出現するせいで一個変数を置いてnullチェックかまさなきゃいけない!というシーンは多いので、predicateじゃなくてnull限定決め打ちでも良いぐらいかもかも。とにかくnull撲滅。まあ、この場合はFirstOrDefaultじゃなくてFirstにすれば、Firstが例外を吐いてくれるのですけど、一応ちゃんとメッセージ用意したいとか、ありますよね?ね?

// 三項演算子のチェーンを崩さないためにダミーの型を返して例外を投げる
static T Throw<T>(Exception exception)
{
    throw exception;
}

static string Test(Fruit fruit)
{
    return (fruit == Fruit.Apple) ? "あぷる"
         : (fruit == Fruit.Grape) ? "ぐれえぷ"
         : (fruit == Fruit.Orange) ? "おれんじ"
         : Throw<string>(new ArgumentException("引数ダメぽ!"));
}

三項演算子を延々とネストしてコロンを前置にするのが好き、と以前に書いたのですが、そうすると、最後にdefault的なものを書く必要があって困る。""とか0とかでお茶を濁さずに、例外を吐きたいのですが、ネスト三項演算子では例外を吐けない。困った困った。というわけでThrowという補助メソッドを用意してみた。ただ例外を吐くだけメソッド。おー。これでもう大量にネストしても大丈夫!万歳!三項演算子でネストしよう!ついでにネスト時のコロンは前置にしよう!の会。

使用しないでください?

ところで、EnumのToStringはIntelliSenseに使用しないでください、が出てきてビビる。んが、よくよく眺めてみると……

public override string ToString();
[Obsolete("The provider argument is not used. Please use ToString().")]
public string ToString(IFormatProvider provider);
public string ToString(string format);
[Obsolete("The provider argument is not used. Please use ToString(String).")]
public string ToString(string format, IFormatProvider provider);

ObsoleteなのはIFormatProviderが絡んでるものだけで、普通のToStringは使用していいんでないのん?文字列の列挙体←ここのコメント欄が白熱していたのを見て、どうなのかなー、と思っているのですが、どうなんでしょうか。いやほんと。

WSH用にCOMのIntelliSenseを自動生成する

本題、の前にJavaScript用のIntelliSenseの作成方法について。以前、linq.jsにIntelliSense用ファイルを追加した時に利用法を書きましたが、今回はvsdocの作成方法を、ざっと書きます。まずVS2008 SP1とパッチを適用する。hoge.jsに対しhoge-vsdoc.jsを用意するとhoge.jsのかわりにhoge-vsdoc.jsがIntelliSense用に読み込まれる。IntelliSenseでVSが見ているのはメソッド名と引数だけなので、コードの中身はなくてもいい。例えば本来は存在するがprivateメソッドのつもりのものは、vsdoc.js側に書かなければIntelliSenseには表示されない。C#と同様に関数にはXMLドキュメントコメントを記述することが出来る。記述箇所はC#とは異なり開始の波括弧の直下。ドキュメントコメントのタグで現在機能しているものはsummary, params, returnsのみ。特に重要なのはreturns typeで、これにprototypeが存在する関数(ようするにクラスですなー)を指定することで戻り値の型をVSに認識させ、IntelliSenseを働かせることが出来る。ちなみに、ただのオブジェクトだと認識してくれない。

function Hoge(aaa, bbb)
{
    /// <summary>hogehogehogehoge</summary>
    /// <param name="aaa" type="String">a!a!a!a!a!</param>
    /// <param name="bbb" type="Optional:Boolean">b?b?b?b?b?</param>
    /// <returns type="Number"></returns>
}

基本はこんな感じ。引数の省略や、型が目で見て分かるので大分書きやすくなります。 で、いつぞやかに作成中とか言っていた通りにlinq.jsをWSH対応させようとしているわけですが、IntelliSense書きの量が思ったよりも膨大なわけです。実は最初は普通に手書きしてました。一応勉強も兼ねて書写みたいなノリで。ですが、あまりにも手間かかりすぎるので自動生成することにしました。こんなの人力でやるもんじゃないですよ。微調整はどちらにせよ必要なのですか、大枠だけでも書きだされていると物凄く楽になる。

static void Main(string[] args)
{
    // add reference and replace dll path
    var assembly = Assembly.LoadFrom(@"Interop.IWshRuntimeLibrary.dll");
    var types = assembly.GetTypes();

    var enums = types
        .Where(t => t.IsEnum)
        .OrderBy(t => t.Name)
        .Select(t => string.Format("{0}: \r\n{{\r\n{1}\r\n}}",
            t.Name,
            Enum.GetNames(t).Select(s => string.Format("\t{0}: {1}", s, (int)Enum.Parse(t, s))).Join(",\r\n")));

    var classes = types
        .Where(t => t.IsClass)
        .Select(type =>
        {
            var bindingFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;

            var properties = type.GetProperties(bindingFlag)
                .OrderBy(pi => pi.Name)
                // .Select(pi => string.Format("{0}: {1}", pi.Name, pi.PropertyType.Name
                .Select(pi => string.Format("{0}: null", pi.Name));

            var methods = type.GetMethods(bindingFlag)
                .Where(mi => !mi.IsSpecialName && mi.Name != "GetEnumerator")
                .Select(mi => new { mi.Name, Parameters = mi.GetParameters(), ReturnType = mi.ReturnType.Name })
                .OrderBy(t => t.Name)
                .Select(t => string.Format("{0}: function({1})\r\n{{\r\n{2}\t/// <returns type=\"{3}\"></returns>\r\n}}",
                    t.Name,
                    t.Parameters.Select(pi => pi.Name).Join(", "),
                    t.Parameters.Select(pi => string.Format("\t/// <param name=\"{0}\" type=\"{1}{2}\"></param>\r\n",
                            pi.Name,
                            (pi.IsOptional) ? "Optional:" : "",
                            pi.ParameterType.Name.Replace("Void", "void").Replace("Int32", "Number")))
                        .Join(""),
                    t.ReturnType.Replace("Void", "void").Replace("Int32", "Number")));

            var result = properties.Concat(methods).Join(",\r\n");
            return string.Format("{0} = function() {{ }}\r\n{0}.prototype =\r\n{{\r\n{1}\r\n}}",
                Regex.Replace(type.Name, "Class$", ""),
                result.Split(new string[] { "\r\n" }, StringSplitOptions.None).Select(s => "\t" + s).Join("\r\n"));
        });

    var name = assembly.GetName().Name.Split('.').Last();
    File.WriteAllText(name + "_enum.js", string.Format("{0}Enum =\r\n{{\r\n{1}\r\n}}", name,
        enums.Join(",\r\n").Split(new string[] { "\r\n" }, StringSplitOptions.None).Select(s => "\t" + s).Join("\r\n")), Encoding.UTF8);
    File.WriteAllText(name + "_class.js", classes.Join("\r\n\r\n"), Encoding.UTF8);
}

static string Join<T>(this IEnumerable<T> source, string separator)
{
    var index = 0;
    return source.Aggregate(new StringBuilder(),
            (sb, o) => (index++ == 0) ? sb.Append(o) : sb.AppendFormat("{0}{1}", separator, o))
        .ToString();
}

参照設定で対象のCOMを読み込んで、一旦ビルド。生成されてるInterop.hoge.dllを読み込んで解析、という流れをとってみました。もっとマシなやり方があれば教えてください。コードは、んーと、string.Formatがクドくて見難いですね! 最初はVSのフォーマッタに後で手動でかければいいや、と思ってたのを、出力状態でちゃんとフォーマットされてるようにと継ぎ接ぎしてたら酷いことに。一回文字列にしたものを改行でバラして再生成とか笑えない。こういうの、HTML/XMLが対象なら何も考えなくてもLinq to Xmlで綺麗に書けるのになあ……。 それ以外はまぁ、こんなものかしらんって感じでしょうか? リフレクションが絡むとLinq大活躍。Selectがネストしまくるのでクエリ構文の出番はない。リフレクションは掘り進める都合上、ネストが深くなるのでLinqがなかったら、と思うとゾッとします。最近はforの二重ループですらウエエ、とか思ってしまうので。

IWshRuntimeLibraryEnum =
{
    IOMode:
    {
        ForReading: 1,
        ForWriting: 2,
        ForAppending: 8
    }
}

Folder = function() { }
Folder.prototype =
{
    Name: null,
    ParentFolder: null,
    Drive: null,
    CreateTextFile: function(FileName, Overwrite, Unicode)
    {
        /// <param name="FileName" type="String"></param>
        /// <param name="Overwrite" type="Optional:Boolean"></param>
        /// <param name="Unicode" type="Optional:Boolean"></param>
        /// <returns type="TextStream"></returns>
    }
}

一部抜粋ですが、こんなようなデータが出力されます。プロパティが全部nullなのは、ここに未指定のものが記述されているとIntelliSenseがエラー起こしてしまうから。例えばDrive: new Drive()で戻り値の型指定が可能といえば可能なのですが、出現位置の上下が問題になってくるので結構面倒くさい。ようはFolderよりも上にDriveがあれば関数が存在するので大丈夫だけど、下にあれば存在しないのでエラー。当たり前といえば当たり前なのですが、ダミーでのIntelliSense作りとしては面倒な問題でして。 この辺は素直に諦めて全部nullで型連鎖を止めてしまうのがお気楽といえばお気楽。あと、Dateはnew Date()にしてもDate.prototypeにしても型指定出来なかったりする問題もある。これは今のところ対処不能。

そんなわけで、linq.js + WSHはわりと順調に作成中なので期待しないでも待っててください。IntelliSenseというだけじゃなく、JScriptではバイト配列が扱いにくいので、扱いやすく出来るような補助メソッドを用意したりとか色々やってたらいつになっても終わらないー。例としてWSHでMD5を作る、とか。.NET Frameworkを通しているのでSHA1でも何でもいけます。これでWSHでwsse認証通してAtomAPIで投稿、とか出来ますね。まあ、もうC#でいいぢゃん、って気がかなりしなくもないですけど、うーん。C#4.0からdynamicが追加されるからCOM連携も楽になるしねえ。ただサイドバーガジェットに使うとか、JavaScriptの用途はまだまだあるもん!(これもSilverlightでやれば?って話がなくもないので、うーん)

簡易文字列置換

static void Main(string[] args)
{
    // {}で括った部分を(ラムダ式/匿名型)を使って置換する
    var input = "食べ物は{tabemono}で飲み物は{nomimono}";
    var r1 = input.Replace(new { tabemono = "たこ焼き", nomimono = "コーラ" });
    var r2 = input.Replace(tabemono => "たこ焼き", nomimono => "コーラ");
}

前も書いたというかJavaScriptでやりましたが、それのC#移植。簡易テンプレート的な文字列置換。ラムダ式版と匿名型版の二つでやってみました。ラムダ式だと、見た目が少し短くて何となく格好良いのですが、変数名の入力時にIntelliSenseが動いてしまうので結構鬱陶しかったり。匿名型のほうはObjectを受け取る関数なので、危なっかしいのが嫌ですね……。

static class Extensions
{
    // 正規表現の|でキーを連結して辞書から置換
    private static string Replace(string input, Dictionary<string, string> dict)
    {
        var pattern = string.Format("{{({0})}}", dict.Select(kvp => Regex.Escape(kvp.Key)).ToJoinedString("|"));
        return Regex.Replace(input, pattern, m => dict[m.Groups[1].Value]);
    }

    /// <param name="anonymousType">{pattern = "replacement"}</param>
    public static string Replace(this string input, Object anonymousType)
    {
        var dict = anonymousType.GetType()
            .GetProperties()
            .ToDictionary(pi => pi.Name, pi => pi.GetValue(anonymousType, null).ToString());
        return Replace(input, dict);
    }

    /// <param name="exprs">pattern => "replacement"</param>
    public static string Replace(this string input, params Expression<Func<Object, string>>[] exprs)
    {
        var dict = exprs.ToDictionary(e => e.Parameters[0].Name, e => e.Compile().Invoke(null));
        return Replace(input, dict);
    }

    // 文字列連結補助メソッド(これないとシンドイので)
    public static string ToJoinedString<T>(this IEnumerable<T> source, string separator)
    {
        var index = 0;
        return source.Aggregate(new StringBuilder(),
                (sb, o) => (index++ == 0) ? sb.Append(o) : sb.AppendFormat("{0}{1}", separator, o))
            .ToString();
    }
}

速度は、匿名型版のほうが圧倒的に速いです。100000回の繰り返しが匿名型だと2秒なのに対してラムダ式版は30秒かかった。Compile().Invokeがアレなんですかねえ。ちなみに、string.Formatでふつーにやった場合は0.03秒でした。まー、でも、メール雛型の置換に、とかちょっとしたことにそこそこ便利に使えるかなー、とは思いつつもっと効率考えてちゃんと組んだ方がいい気もする。

ラムダ式から取り出すのはC# 3.0 における疑似 Map 生成リテラル - NyaRuRuの日記から、匿名型はprototype.jsとかPHPのstrtrとか色々。あとややニッチな Anonymous Types の使い方をまとめてみる (C# 3.0) - NyaRuRuの日記です。

ver 0.0.0.3

拡張子が大文字だとアップロード出来なかったので直しました。XboxInfoTwitの時も同じのやってたのにまたかよって感じです。拡張子判定部分は、ちゃんとIgnoreCaseにしたしあれえ?と思ってたんですがContentType作るところで漏れがあって、ウッカリ。てへ。

まあ、そんなこんなでちゃんと一眼レフも買いました。わざわざこのためだけに!neuecc's fotolife 。それで、しかし撮るものが悲しいほど無いんですよね、やっぱり。とはいえ、自分で撮って自分で上げてかないと、何をどうすれば良くなるのか分からないので、室内写真で栄える何かを探し中です。現在は多肉植物でも育てようかな、と思ってるんですがどうでしょうかねえ。

ちなみに現在までのDL数は余裕で一桁。べ、別に悲しくないもん! そういえばこの半自動ってぶっちゃけ機能的にいらなくね?むしろフォルダ監視で自動化したほうがよくね?とも思ってきたので、まあ、そのうち。そのうち。

ネストした三項演算子の書き方

javaプログラムの組み方がわかりませんorz 無理な課題を学校で出されました(´・_・... - Yahoo!知恵袋

「小学生向けの四則演算の計算練習のプログラムを考える」……? 回答のJavaコードが長くてよくわからにゃい。ぽりもふぃずむって何?

これでおk。最近、三項演算子の:は後置が普通だけど、前置にすると[F#のパターンマッチ](http://msdn.microsoft.com/en-us/library/dd233242(VS.100).aspx)みたいで格好良くね?とか思うので、開き直って前置にしたいです。これはコーディング規約に載せるべきですね(笑) あと三項演算子の、うにゃ、条件演算子の条件は必ずカッコで括る派。

LINQ to XMLのNamespaceと書き出し時のEncodingについて

ver 0.0.0.2に更新。アップロードするフォルダが指定出来るようになりました。アップロードツール名(FotolifeUploader)が利用されるようになりました。フォルダ指定は再設定が必要なので、前のバージョンを使っている方はsettings.xmlを削除して、再度設定し直してください。あとは間抜けだったUploadToFotolifeメソッドを手直ししたり。

私自身が、そもそもフォトライフのヘビーユーザーではないので、細かいところに気が利いてないかもですね……。そういうのは、よくない。というわけで、当分はFotolifeをちゃんと利用しようキャンペーンを張ることにします。なので、デジタル一眼を買う。と言いたいのだけど、何か微妙なのよねん。いや、そもそも引き籠って家から出ないので撮影するものがないので。かといって熱帯魚や食虫植物とかフィギュアとか、撮影に適した趣味があるわけでもなく。困った困った。まあ、考えます。食虫植物を育てる方向で(?) 部屋が殺風景なので何かは入れたいのだけど、手間はかけたくない。ううむ、難しい。

LINQ to XML

アップロードにはAtomAPIを利用しているので、XMLです。つまりLINQ to XMLの出番です。出力結果がこんな感じなので、そこから逆に考えると……

<entry xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/atom/n
s#">
  <title>タイトル</title>
  <content mode="base64" type="image/jpeg">画像BASE64</content>
  <generator>FotolifeUploader</generator>
  <dc:subject>フォルダ名</dc:subject>
</entry>

XElementは、Namespaceの利用が少しややこしいんですよね。最初引っかかりました。「XNamespace.Xmlns + "接頭辞"」で登録できます。

XNamespace ns = "http://purl.org/atom/ns#";
XNamespace dc = "http://purl.org/dc/elements/1.1/";
var xml =
    new XDocument(new XDeclaration("1.0", "UTF-8", null),
    new XElement(ns + "entry", new XAttribute(XNamespace.Xmlns + "dc", dc),
        new XElement(ns + "title", "タイトル"),
        new XElement(ns + "content", new XAttribute("mode", "base64"), new XAttribute("type", "image/jpeg"), "画像BASE64"),
        new XElement(ns + "generator", "FotolifeUploader"),
        new XElement(dc + "subject", "フォルダ名")
    ));
Console.WriteLine(xml); // 出力確認、DeclarationはToStringでは出力されない

少し独特ですが、ほとんど1:1で対応させられるので慣れるとサクサク書けます。非常に快適。個人的にはXMLリテラル的なものよりも好き。Linqがあってほんと良かった……。で、Declarationを出力したい場合の話に続く。(hatena (diary ’Nobuhisa))にもあるように、ToStringでは出力されないのでSaveを使う、と……

var sb = new StringBuilder();
var sw = new StringWriter(sb);
xml.Save(sw);
Console.WriteLine(sb); // UTF-16になる

これでencodingがUTF-16になるのは、Saveメソッド呼ぶとDeclarationは作りなおしているから。.Save("string fileName")ではXDeclarationのエンコーディングを見て、それで保存するけれど、それ以外の場合はXDeclaration無視で再構築される。XDocumentというかXmlWriterのほうの話でしょうか。実際にファイル出力してみると分かる。

var fs = new FileStream(@"C:\text.xml", FileMode.Create);
var sw = new StreamWriter(fs, Encoding.GetEncoding("x-mac-turkish"));
xml.Save(sw);

出力先のエンコードに合わせてくれる、のを便利と見るか、むしろ気が利かない、Writer部分もC#3.0に合わせて作りなおせ、なのかは不明。まあ、嘘エンコード宣言は許しませんよってことですかね。じゃあどうするか、って言ったら

// これで別に何も問題ないと思います、文字列として吐くんだからToStringでいいと思ふ
var xmlString = string.Format("{0}{1}{2}",
    xml.Declaration, Environment.NewLine, xml);
Console.WriteLine(xmlString);

// ToStringがどうしても嫌ならMemoryStream経由で、とか?
string result;
var encoding = Encoding.UTF8;
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms, encoding))
{
    xml.Save(sw);
    result = encoding.GetString(ms.ToArray());
}
Console.WriteLine(result); // 望み通りのUTF-8で出力されてます

結論は、普通にToStringでいいんじゃないかな、と。ToStringメソッドだけではXmlWriterSettingsで言うところのOmitXmlDeclarationを設定出来ないから、デフォルトでは付加しないようにしてる。削除は無理だけど、追加なら簡単だから。XmlDeclarationを付加したい時は別途、自分でくっつければいい。というだけのお話かなー? ToStringで一発で終わらせられないからStringBuilder使って組み立てるってのは、何でそうなるの?と、とても思った。ついでにもう一つ。

// こんなXElementがあるとして
var xElement = XElement.Parse("<hoge>3</hoge>");
// intとして値を取り出す時は
var num1 = int.Parse(xElement.Value); // これダメ。
var num2 = (int)xElement; // こう書こう。

です。LINQ to XMLは既存のものを上手く使ってシンプルに書けるように作られてる。気がする。このキャストもそうだし、ToStringもそう。Parseは頻繁に行うから汚くなるよね→キャストでよくね? 文字列化はよくやるけどSaveもXmlWriterSettingsも面倒くさいよね→ToStringでよくね? といった感じ。関数型構築もそうだけど、今までのもの(XmlDocument)を踏まえて、よく練り直されているなー、と思います。

半自動はてなフォトライフアップローダー ver 0.0.0.1

はてなフォトライフに画像をワンクリックでアップロードするプログラムです。ワンクリックの手間があるので、半自動。主な機能は、実行すると設定したフォルダの最新の更新画像一枚をアップロード。利用例としてデジカメ接続時やメモリーカード内の画像フォルダを指定することを想定しています。写真撮る→PCに繋げる→プログラムを実行する→アップロード完了。みたいな流れです。Twitterに載せるための写真とか最新一枚で十分でしょう? Blogに載せる場合でも、一枚で済む場合って結構多いよね。そんな感じに、サクサクッと写真と付き合えたらいいな、と。

設定

まさかのCUI設定画面(笑) 初回起動時にこの画面になります。設定し直したい時は、生成されるsettings.xmlを削除してください。レトロでアナログで半自動を貫く感じがいいかなー、と思ったんですが、どうでしょう。

最新画像一枚のアップロード

設定終了後にexeファイルを実行すると、設定時に指定したフォルダの中の、拡張子が「jpg/jpeg/gif/png/bmp」で更新日時が最も新しいもの一枚をアップロードします。設定によってはアップロード後にブラウザでフォトライフのURLが開きます。なので、そこからそのままTwitterにURLをポストするなりBlog書くなりがシームレスに行えるわけです。キリッ。ちなみにリサイズ等はこちら側では一切しません、そのまま丸投げ。リサイズ処理もはてな任せ。

任意画像複数枚のアップロード

フォルダ/画像をまとめてexeファイル(本体じゃなくてショートカットでもOKです)にドラッグアンドドロップすると、そのファイルをアップロードします。フォルダはサブディレクトリを含めて全てのファイルをアップロードします。拡張子が「jpg/jpeg/gif/png/bmp」以外のものはちゃんと無視しますので、多少適当でも大丈夫。また、いわゆる「送る」にショートカットを登録することで、このドラッグアンドドロップと同様の結果になります。Vistaの場合はエクスプローラー上で「%AppData%\Microsoft\Windows\SendTo」と入力するとSendToのフォルダに飛べますので、ここにショートカットを登録してみてください。

今回コンソールアプリにしたのは、実行にかかる手間を最小にしたかった、というのがあります。普通のアップロードアプリだと、「アプリを起動→画像フォルダを開く→ドラッグアンドドロップで画像を乗っける→アップロードボタンを押す→アプリを閉じる→Fotolifeにアップロードされた画像を確認しにいく」 これじゃあ工程多すぎであまりにも面倒くさい。というわけで、最新画像一枚ならば、アプリ起動だけで完了。複数毎でも画像フォルダ→ドラッグアンドドロップだけで完了という、考え得る限りの最短を目指しています。

ソースコード

ソースコードも同梱してあります。csファイル一つだけの、200行ちょいのちっぽいコンソールアプリです。好きに改変とか突っ込みとかディスとかしてください。しいていえば、Linqだらけです。個人的には

.SelectMany(s => (Directory.Exists(s))
  ? Directory.GetFiles(s, "*", SearchOption.AllDirectories)
  : Enumerable.Repeat(s, 1))
.Select(s => new FileInfo(s))
.Where(fi => fi.Exists && FotolifeExtensionPattern.IsMatch(fi.Extension))

この部分が気に入ってます。ドラッグアンドドロップで来る文字列配列からファイル抜き出しの部分。SelectManyでディレクトリをファイル名配列に、ディレクトリじゃない場合はEnumerable.Repeatで繰り返し回数が1回のファイル名配列にする。あとはまあ普通に、SelectしてWhereしてToArray。Linqがあって良かったーと本当に思う。逆にAtomPub APIでアップロードする部分はLinqでやる意味がなかったというか、当初予定と変わってあれ追加これ追加で肥大化してしまった結果でして……。

LLの人はこの手のちょっとしたスクリプトをほいほい公開しているわけだから、C#もコンソールアプリぐらいほいほい公開出来ないといかんのぅ、と思いつつもページ用意して云々かんぬんは面倒くさくて、そうホイホイってわけにもいかない感じ。もちっと軽くやれる環境作らないとね……。まあ、でも、このちょっとした重苦しさも悪くはないんだ。だってほら、Rubyでスクリプトがホイッって転がってても、普通の人は動かせもしないわけですよ。だから、少し面倒くさいなー、と思いつつ設定画面つけてexeの形式にして、それだけで幸せになれないかな、どうだろう。

私はプログラム書き始めたのがほんとつい最近で、利用するだけ人間の歴が何年も何年もあるので、その辺は極力優しくやりたいなあ、と思ってます。

Prev | | Next

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

Microsoft MVP for Developer Technologies(.NET)
April 2011
|
July 2026

X:@neuecc GitHub:neuecc

Archive