那是(某种程度上)这个问题.
我最近正在努力学习F#,而且我的资源似乎更倾向于在不清楚存在利益的地方使用管道.例如,给定一个curried形式的函数:
f: a -> b -> c -> R
我可以通过提供所有内联或管道参数来调用它c
:
let r = f a b c
let r = c |> f a b
Run Code Online (Sandbox Code Playgroud)
但是,是否存在成本或收益,还是纯粹的风格偏好?
应该没有成本.
UPDATE
有可能是一些额外的费用.有时会插入一个额外的调用,但有时候编译器可以优化它以避免额外的调用.
我测试了以下代码:
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
Run Code Online (Sandbox Code Playgroud)
对于每个案例,我执行了十亿次调用(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
Run Code Online (Sandbox Code Playgroud)
这两个调用Array.map
是等价的,但第二个调用抱怨它不知道类型w
.这是因为F#类型的推理机制从左到右工作,第二种情况words
是在lambda函数之后.
它在风格上也更好.上面的代码可以更好地表达如下:
"many words".Split ' '
|> Array.map (fun w -> w.ToUpper())
|> Seq.iter (printfn "words are %A")
Run Code Online (Sandbox Code Playgroud)
因此,管道可用于链接多个表达式,而无需使用括号或将其绑定到名称.