通常我们(社区)说 Pipe Operator|>只是一种在函数调用之前写入函数的最后一个参数的方法。例如
f x y
Run Code Online (Sandbox Code Playgroud)
可以写成
y |> f x
Run Code Online (Sandbox Code Playgroud)
但为了正确性,事实并非如此。它只是将下一个参数传递给函数。所以你甚至可以写。
y |> (x |> f)
Run Code Online (Sandbox Code Playgroud)
所有这些以及所有其他类型的运算符都可以工作,因为在 F# 中所有函数都默认被柯里化。这意味着,只存在带有一个参数的函数。具有多个参数的函数是通过一个函数返回另一个函数来实现的。
你也可以写
(f x) y
Run Code Online (Sandbox Code Playgroud)
例如。该函数f是以另一个函数x为参数并返回另一个函数的函数。然后,这将y作为参数传递。
这个过程是由语言自动完成的。所以如果你写
let f x y z = x + y + z
Run Code Online (Sandbox Code Playgroud)
它等同于:
let f = fun x -> fun y -> fun z -> x + y + z
Run Code Online (Sandbox Code Playgroud)
顺便说一句,与类似 LISP 的语言相比,柯里化是类似 ML 的语言中不强制执行括号的原因。否则你需要写:
(((f 1) 2) 3)
Run Code Online (Sandbox Code Playgroud)
执行f带有三个参数的函数。
管道运算符本身只是另一个函数,它定义为
let (|>) x f = f x
Run Code Online (Sandbox Code Playgroud)
它采用一个值x作为其第一个参数。一个函数f作为它的第二个参数。因为运算符是书面的“中缀”(这意味着两个操作数之间)而不是“前缀”(在参数之前,正常方式),这意味着运算符的左侧参数是第一个参数。
在我看来,|>大多数 F# 人用得太多了。如果您有一系列操作,一个接一个,那么使用管道是有意义的。通常,例如,如果您有多个列表操作。
假设您想要对列表中的所有数字进行平方,然后仅过滤偶数。没有管道你就可以写。
List.filter isEven (List.map square [1..10])
Run Code Online (Sandbox Code Playgroud)
这里的第二个参数List.filter是返回的列表List.map。您也可以将其写为
List.map square [1..10]
|> List.filter isEven
Run Code Online (Sandbox Code Playgroud)
管道是函数应用程序,这意味着,您将执行/运行一个函数,因此它会计算并返回一个值作为其结果。
上面的例子中List.map首先执行,并将结果传递给List.filter. 对于有管道和没有管道的情况都是如此。但有时,您想要创建另一个函数,而不是执行/运行函数。假设您想根据上面的内容创建一个函数。您可以编写的两个版本是
let evenSquares xs = List.filter isEven (List.map square xs)
let evenSquares xs = List.map square xs |> List.filter isEven
Run Code Online (Sandbox Code Playgroud)
您也可以将其写为函数组合。
let evenSquares = List.filter isEven << List.map square
let evenSquares = List.map square >> List.filter isEven
Run Code Online (Sandbox Code Playgroud)
该<<运算符类似于“正常”方式的函数组合,即如何编写带括号的函数。以及>>“向后”组合,如何用|>.
F# 文档以另一种方式编写它,即向后和向前。但我认为 F# 语言的创建者错了。
函数组合运算符定义为:
let (<<) f g x = f (g x)
let (>>) f g x = g (f x)
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,从技术上讲,该运算符具有三个参数。但请记住柯里化。当您编写 时f << g,结果是另一个函数,它需要最后一个参数x。传递比需要的更少的参数通常也称为部分应用。
函数组合在 F# 中较少使用,因为如果函数参数是泛型,编译器有时会出现类型推断问题。
理论上,您可以在不定义变量的情况下编写程序,只需通过函数组合即可。这也称为Point-Free 风格。
我不会推荐它,它通常会使代码更难阅读和/或理解。但如果您想将一个函数传递给另一个
高阶函数,有时会使用它。这意味着,一个函数接受另一个函数作为参数。诸如此类List.map。List.filter