方法链接vs |>管道操作员

Li *_*oyi 23 f# map filter method-chaining c#-to-f#

所以我有以下代码:

// Learn more about F# at http://fsharp.net
open System
open System.Linq
open Microsoft.FSharp.Collections

let a = [1; 2; 3; 4; 54; 9]

let c = a |> List.map(fun(x) -> x*3) |> List.filter(fun(x) -> x > 10)
let d = a.Select(fun(x) -> x*3).Where(fun(x) -> x > 10)

for i in c do
    Console.WriteLine(i)

for i in d do
    Console.WriteLine(i)
Run Code Online (Sandbox Code Playgroud)

两者似乎做同样的事情,但我看到的大多数F#例子都使用了|>管道运算符,而我更习惯于方法链接(ala C#Linq).后者也稍微短一些,虽然稍微嘎吱作响.目前我正在使用C#Linq语法,但这更多是习惯/惯性而不是任何真正的设计决策.

是否有任何我应该了解的考虑因素,或者它们基本相同?

编辑:另一个考虑因素是管道语法比Linq语法明显更"嘈杂":我正在做的操作(例如"map")非常短而且是小写的,而每一个前面都有这个巨大的"|>列出"除了使它更长时间分散注意力远离微小的小写方法名称.甚至StackOverflow的语法高亮显示也突出了错误(不相关)的事情.无论是那个还是我都不习惯它.

Dan*_*iel 23

流水线技术支持F#的从左到右类型的推理.a.GroupBy要求的类型a是已知的是seq<_>,而a |> Seq.groupBy本身推断aseq<_>.以下功能:

let increment items = items |> Seq.map (fun i -> i + 1)
Run Code Online (Sandbox Code Playgroud)

需要使用LINQ编写类型注释:

let increment (items:seq<_>) = items.Select(fun x -> x + 1)
Run Code Online (Sandbox Code Playgroud)

随着您对功能风格的熟悉,您将找到使代码更简洁的方法.例如,以前的功能可以缩短为:

let increment = Seq.map ((+) 1)
Run Code Online (Sandbox Code Playgroud)


Tom*_*cek 15

其他人已经解释了两种风格之间的大部分差异.在我看来,最重要的是类型推断(Daniel提到),它更适用于基于流水线和函数的惯用F#风格List.map.

另一个区别是,当使用F#样式时,您可以更容易地看到计算的哪个部分懒惰地评估,何时强制评估等等,因为您可以组合IEnumerable<_>(调用Seq)函数和列表或数组的函数:

let foo input =
  input 
  |> Array.map (fun a -> a) // Takes array and returns array (more efficient)
  |> Seq.windowed 2         // Create lazy sliding window
  |> Seq.take 10            // Take sequence of first 10 elements
  |> Array.ofSeq            // Convert back to array
Run Code Online (Sandbox Code Playgroud)

我还发现|>运算符在语法上更方便,因为我永远不知道如何正确地缩进使用的代码.Foo- 尤其是放置点的位置.另一方面,|>在F#中已经建立了相当完善的编码风格.

一般来说,我建议使用这种|>风格,因为它是"更标准".在F#中使用C#样式没有任何问题,但您可能会发现以更惯用的方式编写代码可以更容易地使用一些有趣的函数式编程概念,这些概念在F#中比在C#中更好.


gjv*_*amp 8

实际上,管道操作员除了交换函数和参数之外什么都不做,据我所知f1 (f2 3),3 |> f2 |> f1除了后者在你将它们链接在一起时更容易阅读之外没有区别.

编辑:它的实际定义如下:let inline (|>) x f = f x.

我猜你倾向于比Linq更多地看到List.map方法的原因是因为在OCaml(F#的前身)中,这些运算符一直存在,所以这种编码风格在功能程序员的思维方式中确实存在.List是F#中一个非常基本的概念,它与IEnumerable(更接近Seq)略有不同.

Linq主要是将这些函数式编程概念引入C#和VB.所以他们在.Net平台上,因此可用,但在F#中他们是多余的.

List.map也是一个非常简单的操作, 而Linq方法带来了懒惰评估等整个框架,带来了一些开销. 但我不认为这会产生重大影响,直到你真正使用它为止.我在一些谈话中听说C#编译器不使用Linq的原因是因为这个原因,但在正常生活中你不太可能注意到.

总而言之,做你最满意的事情,没有对错.我个人会选择List运算符,因为它们在'惯用'F#中更为标准.

GJ

  • `seq <T>`和`IEnumerable <T>`不仅仅是接近,它们是相同的.`seq <T>`只是`IEnumerable <T>`的别名. (5认同)
  • LINQ方法并没有真正带来"整个框架与懒惰的评估".它只是一个语法上的差异,它依赖于`Select`的实现(它不一定是'IEnumerable`的实现).除此之外,您可以使用"Seq.map"使用F#进行延迟评估. (4认同)