这是我在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))
答案 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))
或者简单地使用另一个答案建议的包装器。