在F#中,流水线是什么意思?

时间:2010-02-01 13:58:14

标签: f# functional-programming

我正在阅读Tomas Petricek的这篇文章,它提到了流水线|>,如给出的例子:

> let nums = [1; 2; 3; 4; 5];;
val nums : list<int>

> let odds_plus_ten = 
        nums
        |> List.filter (fun n-> n%2 <> 0)
        |> List.map (add 10)
val odds_plus_ten : list<int> = [11; 13; 15];;

流水线是什么意思?最初,我认为它类似于在内核中流水线化的CPU指令。你能解释它是什么以及它如何在F#的背景下起作用?

谢谢, 最好的祝福, 汤姆。

4 个答案:

答案 0 :(得分:14)

在某些方面,流水线没有什么特别之处;而不是写f (g (h x)),你可以写x |> h |> g |> f,这似乎不是一个明显的改进。但是,有两点值得记住:

  1. 有时读取顺序对于流水线版本更好:“取x并将其发送到h,将结果发送到g,将结果发送到f”比“应用f到应用g的结果”更容易理解将h应用于x“。
  2. 的结果
  3. 对于流水线版本,类型推断通常效果更好。这可能是F#中使用流水线的最大原因。由于类型推断从左到右进行,x |> Array.map (fun s -> s.Length)将在x为string[]时有效,但Array.map (fun s -> s.Length) x则不会;你需要改为Array.map (fun (s:string) -> s.Length) x

答案 1 :(得分:11)

Pipelining意味着将一个函数的结果传递给另一个函数。在示例中,您将“nums”传递给List.Filter,然后将过滤后的结果传递给List.Map。

此处有更多信息:http://msdn.microsoft.com/en-us/magazine/cc164244.aspx#S6

答案 2 :(得分:3)

正如其他人所提到的,流水线操作更像是UNIX shell管道。它让你编写一些输入,然后是应该应用于它的操作,而不是通常的嵌套函数调用。在此示例中,标准F#代码如下所示:

let r = List.map (add 10) (List.filter (fun n-> n%2 <> 0) nums)

请注意,输入nums深深嵌套在表达式中,并且不容易看到它首先被过滤然后进行投影。使用流水线技术,你可以用不同的方式编写代码,但它的意思完全相同。

技巧是流水线操作符使用中缀表示法(例如x |> f)获取两个参数。 x参数将作为最后一个参数传递给右侧的函数(f)。您可以在任何F#函数中使用流水线操作:

let sinOne = 1.0 |> sin

let add a b = a + b
let r = 10 |> add 5 // it doesn't always make code more readable :-)

关于F#流水线操作符的一个重点是它不是该语言的任何特殊内置功能。它是一个简单的自定义运算符,您可以自己定义:

let (|>) x f = f x

// Thanks to operator associativity rules, the following:
let r = 1.0 |> sin |> sqrt
// ...means this:
let r = (1.0 |> sin) |> sqrt

答案 3 :(得分:2)

查看Pipelining in F#以获取解释。

(如果您熟悉unix命令行和管道,如

cat file1 | sort | head 
这是一个类似的想法;前一个表达式的结果成为下一个函数的参数。)