编辑:
这里有一个F#的解决方案:
type DU =
| A
| B
type State = { AValue : int; BValue : int }
let solution2 (aObservable:IObservable<_>, bObservable:IObservable<_>) =
let union = aObservable.Select(fun _ -> A).Merge(bObservable.Select(fun _ -> B))
let result = union.Scan({AValue = 0; BValue = 1}, fun state du -> match du with
| A -> { state with AValue = state.AValue + state.BValue }
| B -> { state with BValue = state.AValue }
)
result
F#实际上是为这个伟大的语言,这要归功于内置的可识别联合和记录。这里有一个用C#编写的答案,带有一个自定义的歧视联盟;我的F#很生锈。
诀窍是使用区分的联合将您的两个observable转换为一个observable。所以基本上团结a和b成一个可观察鉴别联合:
a : *---*---*---**
b : -*-*--*---*---
du: ab-ba-b-a-b-aa
一旦做到这一点,这样你就可以到,如果该项目是一个“A”推或“B”推反应。
只是为了确认,我假设没有办法明确设置嵌入在ButtonA/ButtonB中的值。如果有的话,那些变化应该被模仿为可观察到的,并且也被用于受歧视的工会。
var a = new Subject<Unit>();
var b = new Subject<Unit>();
var observable = a.DiscriminatedUnion(b)
.Scan(new State(0, 1), (state, du) => du.Unify(
/* A clicked case */_ => new State(state.A + state.B, state.B),
/* B clicked case */_ => new State(state.A, state.A)
)
);
observable.Subscribe(state => Console.WriteLine($"a = {state.A}, b = {state.B}"));
a.OnNext(Unit.Default);
a.OnNext(Unit.Default);
a.OnNext(Unit.Default);
a.OnNext(Unit.Default);
b.OnNext(Unit.Default);
a.OnNext(Unit.Default);
a.OnNext(Unit.Default);
a.OnNext(Unit.Default);
a.OnNext(Unit.Default);
b.OnNext(Unit.Default);
下面是在C#中依赖的类。其中大部分很容易转换为内置的F#类型。
public class State /*easily replaced with an F# record */
{
public State(int a, int b)
{
A = a;
B = b;
}
public int A { get; }
public int B { get; }
}
/* easily replaced with built-in discriminated unions and pattern matching */
public static class DiscriminatedUnionExtensions
{
public static IObservable<DiscriminatedUnionClass<T1, T2>> DiscriminatedUnion<T1, T2>(this IObservable<T1> a, IObservable<T2> b)
{
return Observable.Merge(
a.Select(t1 => DiscriminatedUnionClass<T1, T2>.Create(t1)),
b.Select(t2 => DiscriminatedUnionClass<T1, T2>.Create(t2))
);
}
public static IObservable<TResult> Unify<T1, T2, TResult>(this IObservable<DiscriminatedUnionClass<T1, T2>> source,
Func<T1, TResult> f1, Func<T2, TResult> f2)
{
return source.Select(union => Unify(union, f1, f2));
}
public static TResult Unify<T1, T2, TResult>(this DiscriminatedUnionClass<T1, T2> union, Func<T1, TResult> f1, Func<T2, TResult> f2)
{
return union.Item == 1
? f1(union.Item1)
: f2(union.Item2)
;
}
}
public class DiscriminatedUnionClass<T1, T2>
{
private readonly T1 _t1;
private readonly T2 _t2;
private readonly int _item;
private DiscriminatedUnionClass(T1 t1, T2 t2, int item)
{
_t1 = t1;
_t2 = t2;
_item = item;
}
public int Item
{
get { return _item; }
}
public T1 Item1
{
get { return _t1; }
}
public T2 Item2
{
get { return _t2; }
}
public static DiscriminatedUnionClass<T1, T2> Create(T1 t1)
{
return new DiscriminatedUnionClass<T1, T2>(t1, default(T2), 1);
}
public static DiscriminatedUnionClass<T1, T2> Create(T2 t2)
{
return new DiscriminatedUnionClass<T1, T2>(default(T1), t2, 2);
}
}
也许问题是 “可变数据”。我认为反应式编程在功能性风格中最有效。 – duffymo
没有“可变数据”的函数方法将会是首选,我只是不知道在这个例子中如何做到这一点。 – Steve