管道还是不管道?

时间:2018-12-15 22:10:28

标签: f#

这是(某种)问题。

我最近尝试学习F#,而我的资源似乎更喜欢在尚无好处的地方使用管道。例如,给定一个函数形式的函数:

f: a -> b -> c -> R

我可以通过以下方式调用它:以内联或管道c的形式提供所有参数,

let r = f a b c 
let r = c |> f a b

但是有成本或收益,还是纯粹是风格上的偏爱?

2 个答案:

答案 0 :(得分:8)

应该没有费用。

更新

可能会一些额外费用。 有时会插入一个额外的调用,但有时编译器可以优化它以避免额外的调用。

我测试了以下代码:

let concat (a:string) (b:string) = System.String.Concat(a, b)

let f1 g a c = g a c
let f2 g a c = c |> g a

f1     concat "b" "c" \\ 00:00:33.8895616
f2     concat "b" "c" \\ 00:00:34.6051700
       concat "b" "c" \\ 00:00:35.0669532
"c" |> concat "b"     \\ 00:00:35.1948296
f1     (+)    "b" "c" \\ 00:00:35.4796687
f2     (+)    "b" "c" \\ 00:00:49.3227193
       (+)    "b" "c" \\ 00:00:35.0689207
"c" |> (+)    "b"     \\ 00:00:35.8214733

对于每种情况,我都进行了十亿次呼叫(1_000_000_000),而那是时间。如您所见,仅呼叫f2 (+) "b" "c"比其余呼叫慢,这可能与(+)作为使用SRTP的操作员有关。

感谢@PhillipCarter进行澄清。


管道的使用有助于类型推断:

let words = "many words".Split ' '

let wordsU1 = words |> Array.map (fun w -> w.ToUpper())  // ok
wordsU1 |> Seq.iter (printfn "words are %A") 

let wordsU2 = Array.map (fun w -> w.ToUpper()) words     // type annotation needed: (w:string)
Seq.iter (printfn "words are %A") wordsU2

Array.map的两个调用是等效的,但是第二个调用则抱怨它不知道w的类型。这是因为F#类型推断机制从左到右起作用,在第二种情况下,words在lambda函数之后。

从样式上也更好。上面的代码可以这样更好地表达:

"many words".Split ' '
|> Array.map (fun w -> w.ToUpper())
|> Seq.iter (printfn "words are %A")

因此,可以使用管道将多个表达式链接起来,而不必使用括号或将其绑定到名称。

答案 1 :(得分:0)

管道。 Don Syme喜欢管道(proof):)

还请记住,还有||>|||>管道。