如何创建在两个可观察量之间交替的可观察量



如果我有ab的可观察量,并且我想根据第三个可观察量c在两者之间交替,我将如何去做呢? 弹珠图看起来像这样:

a: -a1---------a2----a3--a4------a5--a6-----a7----
b: -b1--b2----b3--b4----------------b5---b6---b7--
c: --------c-----------c--------c----------c------
                    alternate
   -a1--------b3--b4-----a4---------b5---b6-a7----

这很好用:

var query =
    a.Publish(pa =>
        b.Publish(pb =>
            c.StartWith("c")
                .Select((x, n) => n % 2 == 0 ? pa : pb)
                .Switch()));

我使用以下代码进行了测试:

var xs = new []
{
    "a1", "b1", "b2", "c", "b3",
    "a2", "b4", "a3", "c", "a4",
    "c", "a5", "b5", "a6", "b6",
    "c", "a7", "b7",
}
    .ToObservable()
    .Publish();
var a = xs.Where(x => x.StartsWith("a"));
var b = xs.Where(x => x.StartsWith("b"));
var c = xs.Where(x => x.StartsWith("c"));
var query = ...
query.Subscribe(Console.WriteLine);
xs.Connect();

我得到了这个结果:

a1 
b3 
b4 
a4 
b5 
b6 
a7 

xs.Connect()只需要使xs可观察的工作作为生成三个序列的简单方法。

甚至作为扩展方法:

public static IObservable<TSource> Alternate<TSource>(
    this IObservable<TSource> leftSelector,
    bool startLeft,
    IObservable<TSource> left,
    IObservable<TSource> right)
{
    return
        left.Publish(pl =>
            right.Publish(pr =>
                leftSelector.StartWith(default(TSource))
                    .Select((x, n) => (n % 2 == (startLeft ? 0 : 1)) ? pl : pr)
                    .Switch()));
}

这是另一种方式,类似的想法 - 它还允许您指定选择器的起始值。

关键思想是使用 ZipMostRecent 将每个值流与最新的选择器值压缩。然后,我们可以根据选择器值过滤每个压缩流并合并它们。

public static IObservable<TSource> Alternate<TSource>(
    this IObservable<bool> leftSelector,
    bool startLeft,
    IObservable<TSource> left,
    IObservable<TSource> right)
{   
    return leftSelector.Publish(
    selector => 
      Observable.Merge(        
        left.Zip(selector.MostRecent(startLeft), Tuple.Create)
            .Where(l => l.Item2),                      
        right.Zip(selector.MostRecent(startLeft), Tuple.Create)
             .Where(r => !r.Item2)))
      .Select(res => res.Item1);            
}

此外,这是Enigmativity的简洁方法,经过轻微调整以适应此处的输入:

public static IObservable<TSource> Alternate2<TSource>(
    this IObservable<bool> leftSelector,
    bool startLeft,
    IObservable<TSource> left,
    IObservable<TSource> right)
{   
    return
        left.Publish(l =>
            right.Publish(r =>
                leftSelector.StartWith(startLeft)
                    .Select(s => s ? l : r)
                    .Switch()));        
}  

此解决方案将 c 声明为 Observable<bool>

结果Observable根据 c 的最新值发出来自 a 和 b 的值。来自 a 和 b 的值被包装到容器类中以供Merge

var a = Observable.Range(1, 10);
var b = Observable.Range(10, 20);
var merged = a.Select(i => new Container {id = "a", value = i})
              .Merge( b.Select(i => new Container {id = "b", value = i}));
var c = Observable.Return(true); 
var result = merged.CombineLatest( c , (ab, selector ) 
                  => (selector && ab.id == "a") || (!selector && ab.id == "b") ? ab : null)
                   .Where(i => i != null);
public class Container
{
    public string id;
    public int value;
}

相关内容

  • 没有找到相关文章

最新更新