我认为这很容易,但我的大脑现在正在融化......
鉴于以下IObservable<int>
流:
1
1
0
0
0
1
0
0
1
0
1
我想将其拆分为表格的IObservable<IEnumerable<int>>
流
1
1 0 0 0
1 0 0
1 0
1
所以每当有0时,它就会被添加到IEnumerable中,当1出现时,会启动一个新的List;对于我的真正问题,这是一个更清晰的定义。
我认为一个好的解决方案是首先通过IObservable<IObservable<int>>
方法将其转换为Window
,然后使用ToEnumerable
,但不知怎的,我不能让它工作..我使用Zip
和Skip(1)
来获取最后一个元素的差异,我也使用了DistinctUntilChanged()
。我饶了我试过的所有variantes ......
可能我最接近的是这段代码:
int[] ints = new[] { 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1 };
var observable = Observable.Interval(TimeSpan.FromMilliseconds(1000)).Take(11).Select(i => ints[i]);
Subject<int> subject = new Subject<int>();
observable.Subscribe(subject);
var observableDiff = subject.Skip(1).Zip(subject, (n, p) => new { Previous = p, Next = n });
var windows = observable.Window(() => observableDiff.Where(x => x.Next == 1));
int index = 0;
windows.Subscribe(window =>
{
Console.WriteLine(string.Format("new window [{0}] ", index++));
window.Subscribe(number => Console.WriteLine(number));
});
这会带来好的结果,但遗憾的是它最终会崩溃..
new window [0]
1
new window [1]
1
0
0
0
new window [2]
1
0
0
new window [3]
1
0
new window [4]
new window [5]
new window [6]
new window [7]
new window [8]
new window [9]
<-- it goes on here until window ~ [80] with a stackoverflow exception
如果我的代码中的错误不存在,我会实现它......
非常感谢任何帮助。 :)
编辑:我使用Rx-Experimental,但它没有什么区别(用LinqPad检查)。 同样删除了主题,它没有影响任何东西。看来我的新方法(Edit2),你需要一个主题,否则窗口的开始是完全奇怪的。
Edit2:稍微改变了问题,以便更好地突出我的问题,抱歉。还更新了我的解决方案。
答案 0 :(得分:12)
这对我有用:
var ints = (new[] { 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1 }).ToObservable();
var result =
ints
.Publish(ns =>
ns
.Where(n => n == 1)
.Select(n =>
ns.TakeWhile(m => m == 0).StartWith(n).ToArray())
).Merge();
我已使用Publish
来确保将ints
可观察对象视为“热”而不是“冷”。
我的结果如下:
答案 1 :(得分:2)
内置Buffer
似乎非常接近您的需求。源和Buffer调用之间的中间订阅将使您获得Buffer所需的闭包可观察量。
IObservable<IList<T>> Buffer<T>(IObservable<T> source,
Func<T, bool> startNew)
{
return Observable.Create<IList<T>>(
obs =>
{
var starts = new Subject<Unit>();
return source.Do(v =>
{
if (startNew(v))
starts.OnNext(Unit.Default);
})
.Buffer(() => starts)
.Where(v => v != null && v.Count > 0)
.Subscribe(obs);
});
}
答案 2 :(得分:1)
好的,这些也是Rx forums:
的好答案James Miles的建议:
var source = new[] { 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1 }.ToObservable();
var windows =
from window in source
.Buffer(2,1) // create an overlapping buffer with 2 items
.Publish(xs => xs.Window(() => xs.Where(x => x.Last() == 1))) // close the window if the 2nd item is == 1
from result in window
.Select(buffer => buffer.First()) // we are only interested in the first item (the 2nd item might be the 1!)
.ToArray() // aggregate the results of the window
where result.Any() // filter out final (empty) window
select result;
int index = 0;
windows.Subscribe(window =>
{
Console.WriteLine(string.Format("new window [{0}] ", index++));
foreach(var x in window)Console.WriteLine(x);
});
Dave Sexton建议使用Extensions for Reactive Extensions (Rxx)中的Parser类,这似乎是一种更加语义化的方法:
using Rxx.Parsers.Reactive.Linq;
public sealed class SplitLab : BaseConsoleLab
{
protected override void Main()
{
var xs = Observable.Generate(
new[] { 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1 }.GetEnumerator(),
e => e.MoveNext(),
e => e,
e => (int) e.Current,
e => TimeSpan.FromSeconds(.5));
var query = xs.Parse(parser =>
from next in parser
let one = next.Where(value => value == 1)
let other = next.Not(one)
let window = from start in one
from remainder in other.NoneOrMore()
select remainder.StartWith(start)
let windowAsString = window.Join()
select windowAsString);
using (query.Subscribe(TraceLine))
{
WaitForKey();
}
}
}
通往罗马的道路很多..