【问题标题】:Stream Arithmetic with Reactive Extensions带有响应式扩展的流算术
【发布时间】:2013-03-01 01:16:41
【问题描述】:

我目前正在开发一个应用程序,该应用程序通过方程式组合了许多数据流。我想做的是:

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&lt;double&gt; 而不是 ArithmeticStream。

两个可能的问题:

如何将IObservable&lt;double&gt; 透明地转换为 ArithmeticStream?

是否有其他途径可以得到我想要的结果?

【问题讨论】:

  • 假设stream1 是ArithmeticStream,您应该将'var result =` 更改为ArithmeticStream result =。
  • @IAbstract,没有。这就是我想写的代码。我目前尝试使用 ArithmeticStreams 来让这条线工作,但如果有人有办法使用 IObservables 或其他东西来做到这一点,那也可以。

标签: c# system.reactive


【解决方案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);
    }
}

【讨论】:

  • @RichardHein 我发现“创意”和“愚蠢”通常是相辅相成的 :)
  • 干杯,我选择了这个,因为中缀运算符使方程更容易理解(我认为错误更可能来自错误的方程而不是其他任何东西)
【解决方案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));

【讨论】:

    【解决方案3】:

    考虑创建一个接受 3、4、5 等 IObservable 和具有匹配数量的结果选择器函数的 CombineLatest 版本。这将让您将算术运算表达为对双精度的普通旧运算 - 非常简单和干净。

    如果您在实施这些方面需要帮助,请直说,我会举个例子。

    编辑

    我所指的 CombineLatest 重载已经存在,只是没有记录。

    【讨论】:

    • 奇怪的是,它是不具有every-arity 重载的方法之一...(请参阅msdn.microsoft.com/en-us/library/… 并在页面上搜索CombineLatest)
    • 有最多 16 个的 arity 重载,只是没有记录。
    【解决方案4】:

    这不如运算符重载好,但我很确定你不能做你想要的那种运算符重载,因为扩展方法中不支持运算符重载。

    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 给出了一个很好的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-16
      • 2013-01-02
      • 1970-01-01
      相关资源
      最近更新 更多