代码之家  ›  专栏  ›  技术社区  ›  t0mm13b

在F中,管道是什么意思?

  •  7
  • t0mm13b  · 技术社区  · 15 年前

    我在读托马斯·佩特里切克的这篇文章,它提到了流水线 |> 如示例所示:

    > 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 回复  |  直到 15 年前
        1
  •  11
  •   Steve    15 年前

    管道化意味着将一个函数的结果传递给另一个函数。在您给出的例子中,“nums”被传递到list.filter,过滤后的结果被传递到list.map。

    更多信息在这里: http://msdn.microsoft.com/en-us/magazine/cc164244.aspx#S6

        2
  •  14
  •   kvb    15 年前

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

    1. 有时,对于流水线版本,读取顺序更好:“取x并发送到h,将结果发送到g,将结果发送到f”比“将f应用到g应用到h应用到x的结果”更容易理解。
    2. 对于流水线版本,类型推断通常工作得更好。这可能是F中使用管道的最大原因。因为类型推断从左到右进行, x |> Array.map (fun s -> s.Length) X为A时有效 string[] 但是 Array.map (fun s -> s.Length) x 不会的,你得做 Array.map (fun (s:string) -> s.Length) x 相反。
        3
  •  3
  •   Tomas Petricek    15 年前

    正如其他人提到的,管道化更像是一个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
    
        4
  •  2
  •   Brian    15 年前

    退房 Pipelining in F# 为了解释。

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

    cat file1 | sort | head 
    

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