这就是(某种)问题。
我最近正在尝试更多地学习 F#,我的资源似乎更喜欢在不清楚是否存在好处的地方使用管道。例如,给定一个柯里化形式的函数:f: a -> b -> c -> R
我可以通过提供所有参数 inline 或管道 c
来调用它,如下所示:
let r = f a b c
let r = c |> f a b
但是有成本或 yield 还是纯粹的风格偏好?
最佳答案
应该没有成本。
更新
还有 可能是 一些额外的开销。
有时会插入一个额外的调用,但有时编译器可以优化它以避免额外的调用。
我测试了以下代码:
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
对于每种情况,我执行了 10 亿次调用 (
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")
因此管道可用于链接多个表达式,而无需使用括号或将其绑定(bind)到名称。
关于f# - 管还是不管?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53797690/