15

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

let (|>) f g = g f

并像这样使用:

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

然后它似乎做的是把 List.Map 放在后面 (fun x -> x * x * x) 并且不会改变 [2;4;6] 的位置

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

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

但是,这不起作用。

我现在才第一次学习 f#。这让我在阅读一本关于 f# 的书时感到困扰。所以我以后可能会知道我错过了什么,但我还是决定问一下。

很明显,我错过了一些重要的东西。因为我可以轻松地重新创建管道运算符。但我不明白为什么它有效。当我了解更多时,我可能很快就会让自己难堪。那好吧。

4

4 回答 4

19

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

这里解释:

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

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# 调用语法:fg

反转 F# 调用语法:gf

标准:

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

反转:

let var = [2;4;6] |> List.map (fun x -> x * x * x)
于 2012-10-16T18:36:25.780 回答
8

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

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

由于|>将其第一个参数应用于第二个参数,因此这相当于

let result = (List.map (fun x -> x * x)) [2;4;6]
于 2012-10-16T18:49:58.267 回答
5

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

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

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

基本上只是把左边的|>东西放在右边的结尾之后。

于 2012-10-16T19:26:58.987 回答
3

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

现在将此签名投影到您的表达式上[2;4;6] |> List.map (fun x -> x * x * x),您将得到List.map (fun x -> x * x * x) [2;4;6],其中参数是列表[2;4;6],函数是一个参数的部分应用List.map (fun x -> x * x * x)函数。

于 2012-10-16T18:52:49.083 回答