总的想法很简单:你合并P和Q,使用BufferWithCount(2)根据你的逻辑得到的值对,然后流程对:
P.Merge(Q).BufferWithCount(2).Select(values =>
{
var first = values[0];
var second = values[1];
if (first is P && second is P ||
first is Q && second is Q)
{
return 0;
}
if (first is P)
{
return selector(first, second);
}
else // suppose Q, P is a valid sequence as well.
{
return selector(second, first);
}
});
现在最困难的部分是合并P和Q如果他们是不同类型,然后在Select中区分它们。如果它们是同一类型的,你可以使用一些简单的像由Enigmativity提出的方法,即
var pqs =
(from p in ps select new { k = "p", v = p })
.Merge(from q in qs select new { k = "q", v = q });
现在最困难的部分是,如果他们是不同类型的,合并它们,我们需要一些常见的包装类型,像,例如Data.Either从哈斯克尔:
public abstract class Either<TLeft, TRight>
{
private Either()
{
}
public static Either<TLeft, TRight> Create(TLeft value)
{
return new Left(value);
}
public static Either<TLeft, TRight> Create(TRight value)
{
return new Right(value);
}
public abstract TResult Match<TResult>(
Func<TLeft, TResult> onLeft,
Func<TRight, TResult> onRight);
public sealed class Left : Either<TLeft, TRight>
{
public Left(TLeft value)
{
this.Value = value;
}
public TLeft Value
{
get;
private set;
}
public override TResult Match<TResult>(
Func<TLeft, TResult> onLeft,
Func<TRight, TResult> onRight)
{
return onLeft(this.Value);
}
}
public sealed class Right : Either<TLeft, TRight>
{
public Right(TRight value)
{
this.Value = value;
}
public TRight Value
{
get;
private set;
}
public override TResult Match<TResult>(
Func<TLeft, TResult> onLeft,
Func<TRight, TResult> onRight)
{
return onRight(this.Value);
}
}
}
滑稽的是,已经有在System.Reactive.dll类似无论阶级,不幸的是它的内部,所以我们需要我们自己的实现。现在,我们可以将两个P和Q成要么与出液进行(我概括了一点,所以你只能返回任何结果,而不是为int):
public static IObservable<TResult> SmartZip<TLeft, TRight, TResult>(
IObservable<TLeft> leftSource,
IObservable<TRight> rightSource,
Func<TLeft, TRight, TResult> selector)
{
return Observable
.Merge(
leftSource.Select(Either<TLeft, TRight>.Create),
rightSource.Select(Either<TLeft, TRight>.Create))
.BufferWithCount(2)
.Select(values =>
{
// this case was not covered in your question,
// but I've added it for the sake of completeness.
if (values.Count < 2)
{
return default(TResult);
}
var first = values[0];
var second = values[1];
// pattern-matching in C# is really ugly.
return first.Match(
left => second.Match(
_ => default(TResult),
right => selector(left, right)),
right => second.Match(
left => selector(left, right),
_ => default(TResult)));
});
}
这里是一个小的演示所有这些可怕的丑陋的东西。
private static void Main(string[] args)
{
var psource = Observable
.Generate(1, i => i < 100, i => i, i => i + 1)
.Zip(Observable.Interval(TimeSpan.FromMilliseconds(10.0)), (i, _) => i);
var qsource = Observable
.Generate(1, i => i < 100, i => (double)i * i, i => i + 1)
.Zip(Observable.Interval(TimeSpan.FromMilliseconds(30.0)), (i, _) => i);
var result = SmartZip(
psource,
qsource,
(p, q) => q/p).ToEnumerable();
foreach (var item in result)
{
Console.WriteLine(item);
}
}
那么如果一个Q在又一个Q&来自输入的 –
永远不会出现两个Q – Ariel
虽然您可能碰巧知道这一点,但您几乎可以肯定需要考虑在发生实施时应该如何处理。 –