如何在F#中使用Observable.Zip

时间:2014-06-25 19:49:33

标签: f#

这是我在C#中的代码

Observable.Zip(ob1, ob2, (a, b) => a + b);

我正在尝试使用Pipe-Forwarding运算符将其转换为F#

ob1 |> Observable.Zip(ob2, Func<_,_,_> (fun a b -> a + b))

它不会编译,因为它无法获得我想要使用的重载。

有任何线索吗?

以下工作正常,我很好奇我是否可以让管道转发操作员在这里工作。从技术上讲,它应该将左侧的ob1作为第一个参数,并将两个提供的参数作为第二个和第三个参数?

 Observable.Zip (ob1,ob2 ,Func<_,_,_>(fun a b -> a + b))

2 个答案:

答案 0 :(得分:5)

如评论中所述,您可以实现这样的简单包装:

    open System.Reactive.Linq

    let zipWith (f : 'T -> 'U -> 'R) 
                (second: IObservable<'U>) 
                (first: IObservable<'T>)
                : IObservable<'R> =
        Observable.Zip(first, second, f)

并且只需按照您的意愿执行:

ob1 |> zipWith (fun a b -> a+b) ob2

PS:如果你这样做,它看起来更好:

ob1 |> zipWith (+) ob2

答案 1 :(得分:2)

您似乎对管道操作员的工作方式有一些错误的印象,所以我会尝试清除它们。

让我们更简单地输入并说我们有函数foo和bar:

let foo (a, b, f) = f a b
let bar a b f = f a b

foo与Observable.Zip具有或多或少相同的形状,并将元组作为参数(这是在F#中看到C#函数的方式),bar是相同的但是是咖喱。

这有效:

foo (ob1, ob2, fun a b -> a + b)
bar ob1 ob2 (fun a b -> a + b)

这不起作用:

ob1 |> bar ob2 (fun a b -> a + b)

那是因为管道运算符所做的,是取左边的值,并将它作为最后一个参数传递给右边的函数。你需要像这样定义bar:

let bar b f a = f a b

这就是为什么例如List模块中的函数是以实际列表作为最后一个参数传入的方式定义的 - 这使得流水线工作以一种很好的方式工作。

这也不起作用:

ob1 |> foo (ob2, fun a b -> a + b)

除了上一个问题之外,它还需要管道操作员查看元组内部并在那里附加一个值,而这实际上不是它的工作原理。元组是F#中的单个值。适用于该示例的函数将是以下函数:

let foo (b, f) a = f a b

但显然这不是我们想要的。

你仍然可以像这样使用管道方式使用Observable.Zip:

ob1 |> fun x -> Observable.Zip(x, ob2, Func<_,_,_> (fun a b -> a + b))

或者简单地使用另一个答案建议的包装器。