5

私は現在、方程式を介して多くのデータストリームを組み合わせるアプリケーションに取り組んでいます。私がやりたいことは次のようなものです。

var result = (stream1 + stream2) / stream3 + stream4 * 2;

resultストリームのいずれかが更新されるたびに更新される場所。現時点では、これをRxで表現できる唯一の方法は次のとおりです。

var result = stream1.CombineLatest(stream2, (x,y) => x+y)
  .CombineLatest(stream3, (x,y) => x / y)
  .CombineLatest(stream4, (x,y) => x + y*2);

これはそれほど明確ではありません。

私の現在の考えは次のとおりです。

Public class ArithmeticStream : IObservable<double>
{
    public static ArithmeticStream operator +(ArithmeticStream xx, ArithmeticStream yy)
    {
        return Observable.CombineLatest(xx,yy, (x,y) => x + y);
    }
    ...
}

問題は、CombineLatestがIObservable<double>ArithmeticStreamの代わりに返すことです。

考えられる2つの質問:

IObservable<double>を透過的にArithmeticStreamに変換するにはどうすればよいですか?

私が望む結果を得る代替ルートはありますか?

4

4 に答える 4

2

かなり違うので、これを新しい回答として追加します...

したがって、そのオペレーターのオーバーロードルートを実行することに専念している場合は、次のようにする必要があります(まあ、少なくとも1つの方法です)...正直なところ、私はこのアプローチが好きではありません-クエリの記述がはるかに簡潔になりますが、DSL アプローチには同様の「簡潔さ」がありますが、演算子のオーバーロードに依存しないという意味ではるかに明確です。

public static class ArithmeticStreamExt
{
    public static ArithmeticStream Wrap(this IObservable<double> src)
    {
        return new ArithmeticStream(src);
    }
    public static ArithmeticStream Const(this double constValue)
    {
        return new ArithmeticStream(Observable.Return(constValue));
    }
}
public class ArithmeticStream 
{
    private IObservable<double> _inner;

    public ArithmeticStream(IObservable<double> source)
    {
        _inner = source;
    }

    public IObservable<double> Source {get { return _inner; }}

    public static ArithmeticStream operator +(
       ArithmeticStream left, 
       ArithmeticStream right)
    {
        return new ArithmeticStream(
            left._inner.CombineLatest(right._inner, (l, r) => l + r));
    }
    public static ArithmeticStream operator -(
       ArithmeticStream left, 
       ArithmeticStream right)
    {
        return new ArithmeticStream(
            left._inner.CombineLatest(right._inner, (l, r) => l - r));
    }
    public static ArithmeticStream operator *(
       ArithmeticStream left, 
       ArithmeticStream right)
    {
        return new ArithmeticStream(
            left._inner.CombineLatest(right._inner, (l, r) => l * r));
    }
    public static ArithmeticStream operator /(
       ArithmeticStream left, 
       ArithmeticStream right)
    {
        return new ArithmeticStream(
            left._inner.CombineLatest(right._inner, (l, r) => l / r));
    }

    public static ArithmeticStream operator +(
        ArithmeticStream left, 
        IObservable<double> right)
    {
        return new ArithmeticStream(
            left._inner.CombineLatest(right, (l, r) => l + r));
    }
    public static ArithmeticStream operator -(
        ArithmeticStream left, 
        IObservable<double> right)
    {
        return new ArithmeticStream(
            left._inner.CombineLatest(right, (l, r) => l - r));
    }
    public static ArithmeticStream operator *(
        ArithmeticStream left, 
        IObservable<double> right)
    {
        return new ArithmeticStream(
            left._inner.CombineLatest(right, (l, r) => l * r));
    }
    public static ArithmeticStream operator /(
        ArithmeticStream left, 
        IObservable<double> right)
    {
        return new ArithmeticStream(
             left._inner.CombineLatest(right, (l, r) => l / r));
    }
}

そしてテストリグ:

void Main()
{
    var s1 = new Subject<double>();
    var s2 = new Subject<double>();
    var s3 = new Subject<double>();
    var s4 = new Subject<double>();

    var result = (s1.Wrap() + s2) / s3 + (s4.Wrap() * 2.0.Const());
    using(result.Source.Subscribe(Console.WriteLine))
    {
        s1.OnNext(1.0);
        s2.OnNext(2.0);
        s3.OnNext(3.0);
        s4.OnNext(4.0);
    }
}
于 2013-02-28T17:16:41.920 に答える
2

うーん...DSLスタイルで比較的簡単にできると思います(演算子をいじる必要はありません):

public static class Ext
{
    public static IObservable<double> Const(this double constant)
    {
        return Observable.Return(constant);
    }

    public static IObservable<double> Plus(this IObservable<double> left, IObservable<double> right)
    {
        return left.CombineLatest(right, (l,r) => l + r);
    }
    public static IObservable<double> Minus(this IObservable<double> left, IObservable<double> right)
    {
        return left.CombineLatest(right, (l,r) => l - r);
    }
    public static IObservable<double> Times(this IObservable<double> left, IObservable<double> right)
    {
        return left.CombineLatest(right, (l,r) => l * r);
    }
    public static IObservable<double> Over(this IObservable<double> left, IObservable<double> right)
    {
        return left.CombineLatest(right,(l,r) => l / r);
    }
}

したがって、クエリは次のようになります。

var result = (s1.Plus(s2)).Over(s3)
        .Plus(s4)
        .Times(2.0.Const());

または、非常におしゃべりなバリアントの場合:

var verboseResult = 
    (s1.Do(Console.WriteLine).Plus(s2.Do(Console.WriteLine)))
    .Over(s3.Do(Console.WriteLine))
    .Plus(s4.Do(Console.WriteLine))
    .Times(2.0.Const())
    .Do(x => Console.WriteLine("(s1 + s2) / s3 + s4 * 2 = " + x));
于 2013-02-28T04:08:40.197 に答える
1

3、4、5 などの IObservable と一致するアリティを持つ結果セレクター関数を取る CombineLatest のバージョンを作成することを検討してください。これにより、算術演算を double の単純な古い演算として表現できます-非常にシンプルでクリーンです。

それらを実装するのに助けが必要な場合は、そう言ってください。例を挙げます。

編集

私が参照している CombineLatest オーバーロードは既に存在し、文書化されていません。

于 2013-02-28T04:18:58.630 に答える
0

これは演算子のオーバーロードほど良くはありませんが、拡張メソッドでは演算子のオーバーロードがサポートされていないため、必要な種類の演算子のオーバーロードを実行できないと確信しています。

var stream1 = Observable.Generate(1.0, x => x < 1000.0, x => x + 0.5, x => x);
var stream2 = Observable.Generate(1.0, x => x < 1000.0, x => x + 0.25, x => x);
var stream3 = Observable.Generate(1.0, x => x < 1000.0, x => x + 0.125, x => x);
var stream4 = Observable.Generate(1.0, x => x < 1000.0, x => x + 0.0625, x => x);
var result = stream1.CombineLatest(stream2, stream3, stream4, (w, x, y, z) => (w + x) / y + z * 2);

そうでなければ、JerKimballは良い答えを出しました。

于 2013-02-28T16:38:18.350 に答える