ブログトップ 記事一覧 ログイン 無料ブログ開設

かずきのBlog@Hatena このページをアンテナに追加 RSSフィード Twitter

2012-02-09

Reactive Extensions再入門 その33「シーケンスの最後を起点にSkipとTake」

過去記事インデックス

はじめに

今回は、はるか昔「その8」や「その9」で紹介したSkipとTake系のメソッドの仲間のSkipLastとTakeLastについて紹介します。何故、前にやらなかったかというと忘れてましたはい。

SkipLastとTakeLastメソッド

ここでは、SkipLastメソッドとTakeLastメソッドについて説明します。SkipLastメソッドとTakeLastメソッドは名前が示す通りIObservable<T>のシーケンスの最後から指定した数の値をSkipしたりTakeしたりするメソッドです。メソッドのシグネチャは下記に示す通り、どちらも何個の値をSkipするのかTakeするのかを指定する数を渡します。

public static IObservable<T> SkipLast<T>(this IObservable<T> source, int count);
public static IObservable<T> TakeLast<T>(this IObservable<T> source, int count);

SkipLastメソッドの使用例を下記に示します。

Observable
    // 1〜10の値を発行する
    .Range(1, 10)
    // 最後3つをSkip
    .SkipLast(3)
    // 購読して表示
    .Subscribe(
        i => Console.WriteLine("OnNext({0})", i),
        () => Console.WriteLine("OnCompleted()"));

このコードは、1〜10の値を発行して最後の3つの値をSkipしています。実行結果を以下に示します。8,9,10の最後の3つの値が表示されていないことが確認できます。

OnNext(1)
OnNext(2)
OnNext(3)
OnNext(4)
OnNext(5)
OnNext(6)
OnNext(7)
OnCompleted()

次にTakeLastメソッドの使用例を下記に示します。

Observable
    // 1〜10の値を発行する
    .Range(1, 10)
    // 最後3つをTake
    .TakeLast(3)
    // 購読して表示
    .Subscribe(
        i => Console.WriteLine("OnNext({0})", i),
        () => Console.WriteLine("OnCompleted()"));

このコードは、1〜10の値を発行して最後の3つを拾っています。実行結果を以下に示します。SkipLastの例では飛ばされていた8,9,10が表示されていることが確認できます。

OnNext(8)
OnNext(9)
OnNext(10)
OnCompleted()|

このように、SkipLastメソッドとTakeLastメソッドを使うことでIObservable<T>のシーケンスの最後を起点にしてSkipしたりTakeをすることが出来ます。そのため、永遠に終わらないIObservable<T>のシーケンスに対して、このメソッドを呼び出すと永遠に結果が返ってこないので注意が必要です。

2012-02-05

Reactive Extensions再入門 その32「型変換を行うCastとOfTypeメソッド」

過去記事インデックス

はじめに

今回は型変換です!

型変換を行う拡張メソッド

ここでは、IObservable<T>のシーケンスに対して型変換を行うメソッドについて説明します。

Castメソッド

ここではCastメソッドについて説明します。Castメソッドは名前の通り、もとになるIObservable<object>のシーケンスから発行された値をキャストして後続に流します。メソッドのシグネチャを以下に示します。

public static IObservable<T> Cast<T>(this IObservable<object> source);

このメソッドの使用例を下記に示します。

var source = new Subject<object>();
source
    // 値をint型にキャスト
    .Cast<int>()
    // 購読
    .Subscribe(
        i => Console.WriteLine("OnNext({0})", i),
        ex => Console.WriteLine("OnError({0})", ex),
        () => Console.WriteLine("OnCompleted()"));

// int型の値を発行
source.OnNext(1);
source.OnNext(2);
source.OnNext(3);

// 文字列型を発行してみる
source.OnNext("abc");

Castメソッドを使ってobject型からint型に変換してSubscribeで購読しています。そして、1〜3の値を発行したあとに文字列型の”abc”を発行しています。実行結果を以下に示します。

OnNext(1)
OnNext(2)
OnNext(3)
OnError(System.InvalidCastException: 指定されたキャストは有効ではありません。
   場所 System.Reactive.Linq.Observable.<Cast>b__46e[TResult](Object x)
   場所 System.Reactive.Linq.Observable.<>c__DisplayClass408`2.<>c__DisplayClass
40a.<Select>b__407(TSource x))

実行結果からわかる通り、文字列型を発行するとInvalidCastExceptionが発行されてOnErrorに処理がいっていることが確認できます。このようにCastメソッドは、あくまで単純なキャストを行うためのメソッドであることがわかります。

OfTypeメソッド

ここでは、OfTypeメソッドについて説明します。OfTypeメソッドは、もとになるIObservable<object>のシーケンスから指定した型でフィルタリングを行うメソッドになります。メソッドのシグネチャを以下に示します。

public static IObservable<T> OfType<T>(this IObservable<object> source);

このメソッドのコード例を下記に示します。

var source = new Subject<object>();
source
    // int型のみを通過させる
    .OfType<int>()
    // 購読
    .Subscribe(
        i => Console.WriteLine("OnNext({0})", i),
        ex => Console.WriteLine("OnError({0})", ex),
        () => Console.WriteLine("OnCompleted()"));

// int型の値を発行
source.OnNext(1);
source.OnNext(2);
source.OnNext(3);

// 文字列型を発行してみる
source.OnNext("abc");
source.OnNext("4");

// もう一度int型の値を発行して終了
source.OnNext(100);
source.OnCompleted();

OfTypeメソッドを使って、1〜3の値が発行されたあと文字列型で”abc”と”4”が発行されて、100が発行されるシーケンスをフィルタリングして、購読しています。実行結果を以下に示します。

OnNext(1)
OnNext(2)
OnNext(3)
OnNext(100)
OnCompleted()

Castメソッドとは異なり、文字列型が発行されても例外は発生しません。単純に後続に値が流れることなくフィルタリングされます。

Reactive Extensions再入門 その31「時間に関する情報を付与するTimestampとTimeIntervalメソッド」

過去記事インデックス

はじめに

個人的には、単発メソッドの紹介はそろそろおおづめ。今回はデバッグに便利そうなメソッドについて紹介します。

時間に関する情報を付与する拡張メソッド

ここでは、IObservable<T>のシーケンスに対して時間に関する情報を付与するIObservable<T>の拡張メソッドについて説明します。

Timestampメソッド

ここでは、Timesptampメソッドについて説明します。Timestampメソッドは、IObservable<T>のシーケンスに対して、その名の通りタイムスタンプを追加するメソッドです。メソッドのシグネチャを以下に示します。

public static IObservable<Timestamped<T>> Timestamp<T>(this IObservable<T> source);

戻り値がIObservable<Timestamped<T>>型になっています。このTimestamped<T>型は以下のように定義されています。(演算子のオーバーロード等は省いています)

using System;

namespace System.Reactive
{
    public struct Timestamped<T>
    {
        public DateTimeOffset Timestamp { get; }
        public T Value { get; }
    }
}

Timestampプロパティで、時間を取得しValueプロパティで値を取得するシンプルな構造体です。コード例を下記に示します。

var subscriber = Observable
    // 1秒間隔で0〜4の値を発行
    .Generate(0, i => i < 5, i => i + 1, i => i, _ => TimeSpan.FromSeconds(1))
    // タイムスタンプをつける(Timestamped<T>型になる)
    .Timestamp()
    // 購読
    .Subscribe(
        i => Console.WriteLine("Timestamp: {0}, Value: {1}", i.Timestamp, i.Value));

// 終了待ち
Console.WriteLine("Please enter key.");
Console.ReadLine();
subscriber.Dispose();

1秒間隔で0〜4の値を発行して、それにTimestampメソッドを呼び出してTimestamped<T>型に変換しています。そしてSubscribeでTimestampとValueを表示しています。実行結果を以下に示します。

Please enter key.
Timestamp: 2012/02/05 22:47:59 +09:00, Value: 0
Timestamp: 2012/02/05 22:48:00 +09:00, Value: 1
Timestamp: 2012/02/05 22:48:01 +09:00, Value: 2
Timestamp: 2012/02/05 22:48:02 +09:00, Value: 3
Timestamp: 2012/02/05 22:48:03 +09:00, Value: 4

値が発行された時の時間が表示されていることが確認できます。

TimeIntervalメソッド

ここでは、TimeIntervalメソッドについて説明します。TimeIntervalメソッドはIObservable<T>のシーケンスから発行された値の間隔をTimeSpan型で追加します。メソッドのシグネチャを以下に示します。

public static IObservable<TimeInterval<T>> TimeInterval<T>(this IObservable<T> source);

戻り値が、IObservable<TimeInterval<T>>型になっています。TimeInterval<T>型は以下のように定義されています。(演算子のオーバーロード等は省いています)

using System;

namespace System.Reactive
{
    public struct TimeInterval<T>
    {
        public TimeSpan Interval { get; }
        public T Value { get; }
    }
}

Intervalプロパティで時間間隔を取得し、Valueプロパティで値を取得するシンプルな構造体です。コード例を下記に示します。

var subscriber = Observable
    // 1秒間隔で0〜4の値を発行
    .Generate(0, i => i < 5, i => i + 1, i => i, _ => TimeSpan.FromSeconds(1))
    // 値の発行間隔をつける(TimeInterval<T>型になる)
    .TimeInterval()
    // 購読
    .Subscribe(
        i => Console.WriteLine("Interval: {0}, Value: {1}", i.Interval, i.Value));

// 終了待ち
Console.WriteLine("Please enter key.");
Console.ReadLine();
subscriber.Dispose();

1秒間隔で0〜4の値を発行して、それにTimeIntervalメソッドを呼び出してTimeInterval<T>型に変換しています。そしてSubscribeでIntervalとValueを表示しています。実行結果を以下に示します。

Please enter key.
Interval: 00:00:01.0050575, Value: 0
Interval: 00:00:01.0150581, Value: 1
Interval: 00:00:01.0140580, Value: 2
Interval: 00:00:01.0140580, Value: 3
Interval: 00:00:01.0120578, Value: 4

値が発行された間隔が表示されていることが確認できます。

Reactive Extensions再入門 その30「もう待ちきれない!を表現するTimeoutメソッド」

過去記事インデックス

はじめに

長いもので30回目です!今回はTimeoutメソッドを紹介したいと思います。

Timeoutメソッド

ここでは、Timeoutメソッドについて説明します。Timeoutメソッドは名前の通りIObservable<T>のシーケンスから指定した時間、値が発行されなかった場合にエラーにするメソッドです。シグネチャを以下に示します。

public static IObservable<T> Timeout<T>(
    this IObservable<T> source, 
    TimeSpan dueTime);

dueTimeでタイムアウトの時間を指定します。TimeSpan以外にもDateTimeOffsetでタイムアウトを指定するオーバーロードがありますが、ここでは紹介を割愛します。このメソッドのコード例を下記に示します。

var subscriber = Observable
    // 0〜4の値をi秒間隔で発行する
    .Generate(0, i => i < 5, i => i + 1, i => i, i => TimeSpan.FromSeconds(i))
    // 3500ms以上間隔があくとタイムアウト
    .Timeout(TimeSpan.FromMilliseconds(3500))
    // 購読
    .Subscribe(
        i => Console.WriteLine("{0:HH:mm:ss} OnNext({1})", DateTime.Now, i),
        ex => Console.WriteLine("{0:HH:mm:ss} OnError({1})", DateTime.Now, ex),
        () => Console.WriteLine("{0:HH:mm:ss} OnCompleted()", DateTime.Now));
// Enterを押すと購読終了
Console.ReadLine();
subscriber.Dispose();

このコードでは、Generateメソッドを使って0, 1, 2, 3, 4の値を0s, 1s, 2s, 3s, 4s間隔で発行しています。それに対してTimeoutメソッドで3.5s(3500ms)を指定しています。このコードの実行結果を以下に示します。

12:39:48 OnNext(0)
12:39:49 OnNext(1)
12:39:51 OnNext(2)
12:39:54 OnNext(3)
12:39:58 OnError(System.TimeoutException: 操作がタイムアウトしました。)

最後の4が発行されるのに4秒間が空いてしまうので、TimeoutExceptionが発行されSubscribeのOnErrorに処理がいきます。このように、時間を要する処理のタイムアウトを簡単に指定することが出来ます。

このTimeoutメソッドにはタイムアウト時の動作をカスタマイズすることが出来るオーバーロードがあります。そのメソッドのシグネチャを以下に示します。

public static IObservable<T> Timeout<T>(
    this IObservable<T> source, 
    TimeSpan dueTime, 
    IObservable<T> other);

第三引数のotherで、タイムアウトが起きたときに代わりに値を発行するIObservable<T>のシーケンスを指定します。このオーバーロードの使用例を下記に示します。

var subscriber = Observable
    // 0〜4の値をi秒間隔で発行する
    .Generate(0, i => i < 5, i => i + 1, i => i, i => TimeSpan.FromSeconds(i))
    // 3500ms以上間隔があくとタイムアウト
    .Timeout(
        TimeSpan.FromMilliseconds(3500), 
        // タイムアウトの時に流す値を指定
        Observable.Create<int>(o =>
        {
            Console.WriteLine("{0:HH:mm:ss} Error Action", DateTime.Now);
            // -1を流して完了
            o.OnNext(-1);
            o.OnCompleted();
            return Disposable.Empty;
        }))
    // 購読
    .Subscribe(
        i => Console.WriteLine("{0:HH:mm:ss} OnNext({1})", DateTime.Now, i),
        ex => Console.WriteLine("{0:HH:mm:ss} OnError({1})", DateTime.Now, ex),
        () => Console.WriteLine("{0:HH:mm:ss} OnCompleted()", DateTime.Now));
            
// Enterを押すと購読終了
Console.ReadLine();
subscriber.Dispose();

TimeoutメソッドでObservable.Createメソッドを使ってタイムアウトの時に-1の値を流してIObservable<T>のシーケンスを完了させるようにしています。このコードの実行結果を以下に示します。

13:08:09 OnNext(0)
13:08:10 OnNext(1)
13:08:12 OnNext(2)
13:08:15 OnNext(3)
13:08:19 Error Action
13:08:19 OnNext(-1)
13:08:19 OnCompleted()

最初のコード例ではTimeoutExceptionが発生していましたが、この例ではObservable.Createで作成したIObservableから発行された値が後続に流れていることが確認できます。このようにタイムアウトとタイムアウトに伴って発行する値を差し替えたりすることが出来ます。

2012-02-04

void*にdeleteしてもデストラクタが呼ばれない!?

という呟きをTwitterで見たので試してみました。実に数年ぶりのC++です!まぁ、こんな小さなサンプル書くだけでもコンパイルエラー出したので、もうダメだなと思いましたorz

#include <iostream>

class A
{
public:
    A()
    {
        std::cout << "A::A()" << std::endl;
    }

    virtual ~A()
    {
        std::cout << "~A::A()" << std::endl;
    }
};

int main(int argc, char** argv)
{
    {
        std::cout << "scope" << std::endl;
        A();
    }
    {
        std::cout << "delete" << std::endl;
        A* ap = new A();
        delete ap;
    }
    {
        std::cout << "void* delete" << std::endl;
        void* ap = new A();
        delete ap;
    }
}

スコープで死んでもらうのと、明示的にdeleteするのとvoid*に代入してdeleteする3ケース試してます。

scope
A::A()
~A::A()
delete
A::A()
~A::A()
void* delete
A::A()

ほんとだ、最後に~A::A()が呼ばれてない。知らなかったな〜。

2012-02-03

Reactive Extensions再入門 その29「値を指定した時間だけ遅延させるDelayメソッド」

過去記事インデックス

はじめに

今回は、Delayメソッドです。Reactive Extensionsのデモとして、とても魅せやすいメソッドですね!シンプルなのでさくっといきましょう。

Delayメソッド

ここでは、Delayメソッドについて説明します。Delayメソッドは名前が示す通りIObservable<T>のシーケンスから発行された値を指定した時間だけ遅延させて後続に流すメソッドになります。

メソッドのシグネチャを以下に示します。

public static IObservable<T> Delay<T>(
    this IObservable<T> source, 
    TimeSpan dueTime);

引数のdueTimeで遅延させる時間を指定します。TimeSpan型を受け取る以外にも、特定の時点まで遅延させることも出来ます。このメソッドのコード例を下記に示します。

var source = new Subject<int>();
source
    // 10秒遅延させる
    .Delay(TimeSpan.FromSeconds(10))
    // 値を時間つきで表示させる
    .Subscribe(i =>
        Console.WriteLine("{0:HH:mm:ss.fff} {1}", DateTime.Now, i));

// 1秒間隔で1〜5の値を発行
foreach (var i in Enumerable.Range(1, 5))
{
    Console.WriteLine("{0:HH:mm:ss.fff} OnNext({1})", DateTime.Now, i);
    source.OnNext(i);
    Thread.Sleep(1000);
}

// 終了待ち
Console.WriteLine("Please enter key");
Console.ReadLine();

Delayメソッドを使ってIObservable<int>のシーケンスから発行された値を10秒遅延させています。実行結果を以下に示します。

21:58:12.174 OnNext(1)
21:58:13.202 OnNext(2)
21:58:14.202 OnNext(3)
21:58:15.202 OnNext(4)
21:58:16.202 OnNext(5)
Please enter key
21:58:22.214 1
21:58:23.205 2
21:58:24.202 3
21:58:25.216 4
21:58:26.215 5

実行結果からわかるように、OnNextで発行した値が10秒後に表示されています。このようにDelayメソッドを使うと、値を遅延させて後続に流すということが実現できます。

2012-02-02

Reactive Extensions再入門 その28「落ち着いたら流すThrottleメソッド」

過去記事インデックス

はじめに

今回はThrottleメソッドです!さくっといきましょう。

Throttleメソッド

ここでは、Throttleメソッドについて説明します。Throttleメソッドは指定した間、新たな値が発行されなかったら最後に発行された値を後続に流すメソッドです。メソッドのシグネチャを以下に示します。

public static IObservable<T> Throttle<T>(
    this IObservable<T> source, 
    TimeSpan dueTime);

第二引数のdueTimeで後続に値を流すための判断基準になる間隔を指定します。このメソッドの使用例を下記に示します。

var source = new Subject<int>();
// 500ms値が発行されなかったら最後に発行された値を後続に流す
source
    .Throttle(TimeSpan.FromMilliseconds(500))
    // 渡ってきた値を時間つきで表示
    .Subscribe(i =>
        Console.WriteLine("{0:HH:mm:ss.fff} {1}", DateTime.Now, i));

// 100ms間隔で値を発行
foreach (var i in Enumerable.Range(1, 10))
{
    // 発行した値を出力しておく
    Console.WriteLine("{0:HH:mm:ss.fff} OnNext({1})",DateTime.Now, i);
    source.OnNext(i);
    Thread.Sleep(100);
}

// 2000ms sleep
Console.WriteLine("{0:HH:mm:ss.fff} Sleep(2000)", DateTime.Now);
Thread.Sleep(2000);

// 100ms間隔で値を発行
foreach (var i in Enumerable.Range(1, 5))
{
    // 発行した値を出力しておく
    Console.WriteLine("{0:HH:mm:ss.fff} OnNext({1})", DateTime.Now, i);
    source.OnNext(i);
    Thread.Sleep(100);
}

// 2000ms sleep
Console.WriteLine("{0:HH:mm:ss.FFF} Sleep(2000)", DateTime.Now);
Thread.Sleep(2000);

Throttleメソッドで500msの間、値が発行されなかった場合に後続に値を流すようにしています。そのあと、foreachで値を10個100ms間隔で発行して2000msスリープしています。次に、値を5個100ms間隔で発行して、2000msスリープしています。実行結果を以下に示します。

23:05:36.003 OnNext(1)
23:05:36.116 OnNext(2)
23:05:36.217 OnNext(3)
23:05:36.317 OnNext(4)
23:05:36.417 OnNext(5)
23:05:36.517 OnNext(6)
23:05:36.617 OnNext(7)
23:05:36.717 OnNext(8)
23:05:36.817 OnNext(9)
23:05:36.917 OnNext(10)
23:05:37.023 Sleep(2000)
23:05:37.420 10           <- ここ
23:05:39.026 OnNext(1)
23:05:39.126 OnNext(2)
23:05:39.226 OnNext(3)
23:05:39.326 OnNext(4)
23:05:39.426 OnNext(5)
23:05:39.526 Sleep(2000)
23:05:39.926 5           <- ここ

実行結果で「<- ここ」で示している箇所が、Subscribeで出力している箇所です。直前のOnNextから500ms後に出力されていることが確認できます。

このThrottleメソッドは、TextBoxの入力が終わって1秒後に自動で検索処理が実行されるケースなどで使います。