为什么管道操作员工作?

时间:2012-10-16 18:32:44

标签: f#

如果管道运算符是这样创建的:

let (|>) f g = g f

并像这样使用:

let result = [2;4;6] |> List.map (fun x -> x * x * x)

那么它似乎做的是将List.Map放在后面(有趣的x - > x * x * x) 并且不会改变[2; 4; 6]

的位置

所以现在它看起来像这样:

let result2 = [2;4;6] (fun x -> x * x * x) List.map

然而这不起作用。

我现在第一次学习f#。这在读一本关于f#的书时困扰着我。所以我可能会学到我后来失踪的东西,但无论如何我决定要问。

很明显,我错过了一些重要的东西。因为我可以轻松地重新创建管道操作员。但我不明白为什么它有效。当我了解更多信息时,我可能很快就会为自己感到尴尬。哦,好吧。

4 个答案:

答案 0 :(得分:13)

管道运算符只是链式方法调用的语法糖。它与linq表达式在C#中的表达方式非常相似。

来自here的解释:

正向管道操作员 我喜欢这个人。 Forward管道运算符简单定义为:

let (|>) x f = f x

并且有一个类型签名:

'a -> ('a -> 'b) -> 'b

转换为:给定泛型类型'a,以及带'a并返回'b的函数,然后在输入上返回函数的应用程序。

让我举一个可以使用它的例子,而不是解释这个:

// Take a number, square it, then convert it to a string, then reverse that string
let square x         = x * x
let toStr (x : int)  = x.ToString()
let rev   (x : string) = new String(Array.rev (x.ToCharArray()))

// 512 -> 1024 -> "1024" -> "4201"
let result = rev (toStr (square 512))

代码非常简单,但请注意语法看起来多么不守规矩。我们要做的就是获取一次计算的结果并将其传递给下一次计算。我们可以通过引入一系列新变量来重写它:

let step1 = square 512
let step2 = toStr step1
let step3 = rev step2
let result = step3

但现在你需要保持所有这些临时变量。 (|>)运算符的作用是获取一个值,并将其“转发”给函数,实质上允许您在函数调用之前指定函数的参数。这大大简化了F#代码,允许您将函数一起管道,其中一个函数的结果传递到下一个函数。因此,要使用相同的示例,代码可以清楚地写为:

let result = 512 |> square |> toStr |> rev

修改

在F#中,您实际使用方法调用时正在执行一个函数,然后将其应用于后面的参数,因此在您的示例中,List.map (fun x -> x * x * x)将应用于[2;4;6]。管道操作员所做的就是以相反的顺序获取参数,然后将应用程序反转回来。

功能:List.map (fun x -> x * x * x) 参数:[2;4;6]

标准F#调用语法:f g

反向F#调用语法:g f

标准

let var = List.map (fun x -> x * x * x) [2;4;6]

反转:

let var = [2;4;6] |> List.map (fun x -> x * x * x)

答案 1 :(得分:8)

|>周围的括号表示它是一个中缀运算符,因此您的示例可以写成

let result = (|>) [2;4;6] (List.map (fun x -> x * x * x))

由于|>将第一个参数应用于第二个参数,因此等效于

let result = (List.map (fun x -> x * x)) [2;4;6]

答案 2 :(得分:5)

正如其他人上面所说,基本上你误解了结果2会解决的问题。它实际上会解决

List.map (fun x -> x * x * x) [2;4;6]

List.map有两个参数:一个应用于列表中所有元素和列表的函数。 (fun x -> x * x * x)是第一个参数,[2;4;6]是第二个参数。

基本上只是在右边的内容之后放置|>左边的内容。

答案 3 :(得分:3)

如果在fsi中输入|>的定义并查看由类型推断派生的运算符签名,您会注意到val ( |> ) : 'a -> ('a -> 'b) -> 'b,即参数'a被赋予函数{{1产生('a -> 'b)

现在将此签名投影到表达式'b上,您将得到[2;4;6] |> List.map (fun x -> x * x * x),其中参数为list List.map (fun x -> x * x * x) [2;4;6],函数为partially applied一个参数的函数{ {1}}。