这就是(某种)问题。

我最近正在尝试更多地学习 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/

10-13 04:53