gpt4 book ai didi

Haskell 性能 : Composition vs Application?

转载 作者:行者123 更新时间:2023-12-04 10:14:21 25 4
gpt4 key购买 nike

我看到了一些关于函数组合和应用程序之间的异同以及各种方法的问题,但有一件事开始让我有点困惑(就我搜索而言,这还没有被问到)是关于表现。

当我学习 F# 时,我爱上了管道运算符 |> ,它在 haskell 的反向应用程序中是等价的 & .但在我看来,F# 变体无疑更漂亮(而且我不认为我是唯一一个)。

现在,可以轻松地将管道操作符破解为 haskell:

(|>) x f = f x

它就像一个魅力!问题解决了!

管道(在 F# 和我们的 haskell 技巧中)之间的最大区别在于它不组合函数,它基于函数应用程序。它获取左侧的值并将其传递给右侧的函数,而不是组合,它接受 2 个函数并返回另一个函数,然后可以将其用作任何常规函数。

至少对我来说,这使得代码更漂亮,因为您只使用一个运算符来引导整个函数中的信息流,从参数到最终值,因为使用基本组合(或 >>>),您无法为左侧让它通过“链”。

但是从性能的角度来看,看看这些通用选项,结果应该是完全相同的:
f x = x |> func1 |> func2 |> someLambda |> someMap |> someFold |> show

f x = x & (func1 >>> func2 >>> someLambda >>> someMap >>> someFold >>> show)

f x = (func1 >>> func2 >>> someLambda >>> someMap >>> someFold >>> show) x

哪一个最快,基于重复应用的一个或基于组合和单个应用的一个(或多个)?

最佳答案

应该没有什么区别,只要(|>)(>>>)内联。让我们编写一个使用四个不同函数的示例,两个是 F# 风格,两个是 Haskell 风格:

import Data.Char (isUpper)

{-# INLINE (|>) #-}
(|>) :: a -> (a -> b) -> b
(|>) x f = f x

{-# INLINE (>>>) #-}
(>>>) :: (a -> b) -> (b -> c) -> a -> c
(>>>) f g x = g (f x)

compositionF :: String -> String
compositionF = filter isUpper >>> length >>> show

applicationF :: String -> String
applicationF x = x |> filter isUpper |> length |> show

compositionH :: String -> String
compositionH = show . length . filter isUpper

applicationH :: String -> String
applicationH x = show $ length $ filter isUpper $ x

main :: IO ()
main = do
getLine >>= putStrLn . compositionF -- using the functions
getLine >>= putStrLn . applicationF -- to make sure that
getLine >>= putStrLn . compositionH -- we actually get the
getLine >>= putStrLn . applicationH -- corresponding GHC core

如果我们用 -ddump-simpl -dsuppress-all -O0 编译我们的代码我们得到:

==================== Tidy Core ====================
Result size of Tidy Core = {terms: 82, types: 104, coercions: 0}

-- RHS size: {terms: 9, types: 11, coercions: 0}
>>>_rqe
>>>_rqe =
\ @ a_a1cE @ b_a1cF @ c_a1cG f_aqr g_aqs x_aqt ->
g_aqs (f_aqr x_aqt)

-- RHS size: {terms: 2, types: 0, coercions: 0}
$trModule1_r1gR
$trModule1_r1gR = TrNameS "main"#

-- RHS size: {terms: 2, types: 0, coercions: 0}
$trModule2_r1h6
$trModule2_r1h6 = TrNameS "Main"#

-- RHS size: {terms: 3, types: 0, coercions: 0}
$trModule
$trModule = Module $trModule1_r1gR $trModule2_r1h6

-- RHS size: {terms: 58, types: 73, coercions: 0}
main
main =
>>
$fMonadIO
(>>=
$fMonadIO
getLine
(. putStrLn
(>>>_rqe
(>>>_rqe (filter isUpper) (length $fFoldable[]))
(show $fShowInt))))
(>>
$fMonadIO
(>>=
$fMonadIO
getLine
(. putStrLn
(\ x_a10M ->
show $fShowInt (length $fFoldable[] (filter isUpper x_a10M)))))
(>>
$fMonadIO
(>>=
$fMonadIO
getLine
(. putStrLn
(. (show $fShowInt) (. (length $fFoldable[]) (filter isUpper)))))
(>>=
$fMonadIO
getLine
(. putStrLn
(\ x_a10N ->
show $fShowInt (length $fFoldable[] (filter isUpper x_a10N)))))))

-- RHS size: {terms: 2, types: 1, coercions: 0}
main
main = runMainIO main

所以 >>>如果我们不启用优化,则不会内联。但是,如果我们启用优化,您将看不到 >>>(.)一点也不。我们的函数略有不同,因为 (.)在那个阶段没有被内联,但这在某种程度上是意料之中的。

如果我们添加 {-# NOINLINE … #-}到我们的函数并启用优化,我们看到这四个函数根本没有区别:

$ ghc -ddump-simpl -dsuppress-all -O2 Example.hs
[1 of 1] Compiling Main ( Example.hs, Example.o )

==================== Tidy Core ====================
Result size of Tidy Core = {terms: 261, types: 255, coercions: 29}

-- RHS size: {terms: 2, types: 0, coercions: 0}
$trModule2
$trModule2 = TrNameS "main"#

-- RHS size: {terms: 2, types: 0, coercions: 0}
$trModule1
$trModule1 = TrNameS "Main"#

-- RHS size: {terms: 3, types: 0, coercions: 0}
$trModule
$trModule = Module $trModule2 $trModule1

Rec {
-- RHS size: {terms: 29, types: 20, coercions: 0}
$sgo_r574
$sgo_r574 =
\ sc_s55y sc1_s55x ->
case sc1_s55x of _ {
[] -> I# sc_s55y;
: y_a2j9 ys_a2ja ->
case y_a2j9 of _ { C# c#_a2hF ->
case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
-> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
(ord# c#_a2hF) realWorld#
of _ { (# ds_a2hJ, ds1_a2hK #) ->
case ds1_a2hK of _ {
__DEFAULT -> $sgo_r574 (+# sc_s55y 1#) ys_a2ja;
0# -> $sgo_r574 sc_s55y ys_a2ja
}
}
}
}
end Rec }

-- RHS size: {terms: 15, types: 14, coercions: 0}
applicationH
applicationH =
\ x_a12X ->
case $sgo_r574 0# x_a12X of _ { I# ww3_a2iO ->
case $wshowSignedInt 0# ww3_a2iO []
of _ { (# ww5_a2iS, ww6_a2iT #) ->
: ww5_a2iS ww6_a2iT
}
}

Rec {
-- RHS size: {terms: 29, types: 20, coercions: 0}
$sgo1_r575
$sgo1_r575 =
\ sc_s55r sc1_s55q ->
case sc1_s55q of _ {
[] -> I# sc_s55r;
: y_a2j9 ys_a2ja ->
case y_a2j9 of _ { C# c#_a2hF ->
case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
-> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
(ord# c#_a2hF) realWorld#
of _ { (# ds_a2hJ, ds1_a2hK #) ->
case ds1_a2hK of _ {
__DEFAULT -> $sgo1_r575 (+# sc_s55r 1#) ys_a2ja;
0# -> $sgo1_r575 sc_s55r ys_a2ja
}
}
}
}
end Rec }

-- RHS size: {terms: 15, types: 15, coercions: 0}
compositionH
compositionH =
\ x_a1jF ->
case $sgo1_r575 0# x_a1jF of _ { I# ww3_a2iO ->
case $wshowSignedInt 0# ww3_a2iO []
of _ { (# ww5_a2iS, ww6_a2iT #) ->
: ww5_a2iS ww6_a2iT
}
}

Rec {
-- RHS size: {terms: 29, types: 20, coercions: 0}
$sgo2_r576
$sgo2_r576 =
\ sc_s55k sc1_s55j ->
case sc1_s55j of _ {
[] -> I# sc_s55k;
: y_a2j9 ys_a2ja ->
case y_a2j9 of _ { C# c#_a2hF ->
case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
-> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
(ord# c#_a2hF) realWorld#
of _ { (# ds_a2hJ, ds1_a2hK #) ->
case ds1_a2hK of _ {
__DEFAULT -> $sgo2_r576 (+# sc_s55k 1#) ys_a2ja;
0# -> $sgo2_r576 sc_s55k ys_a2ja
}
}
}
}
end Rec }

-- RHS size: {terms: 15, types: 15, coercions: 0}
compositionF
compositionF =
\ x_a1jF ->
case $sgo2_r576 0# x_a1jF of _ { I# ww3_a2iO ->
case $wshowSignedInt 0# ww3_a2iO []
of _ { (# ww5_a2iS, ww6_a2iT #) ->
: ww5_a2iS ww6_a2iT
}
}

Rec {
-- RHS size: {terms: 29, types: 20, coercions: 0}
$sgo3_r577
$sgo3_r577 =
\ sc_s55d sc1_s55c ->
case sc1_s55c of _ {
[] -> I# sc_s55d;
: y_a2j9 ys_a2ja ->
case y_a2j9 of _ { C# c#_a2hF ->
case {__pkg_ccall base-4.9.1.0 u_iswupper Int#
-> State# RealWorld -> (# State# RealWorld, Int# #)}_a2hE
(ord# c#_a2hF) realWorld#
of _ { (# ds_a2hJ, ds1_a2hK #) ->
case ds1_a2hK of _ {
__DEFAULT -> $sgo3_r577 (+# sc_s55d 1#) ys_a2ja;
0# -> $sgo3_r577 sc_s55d ys_a2ja
}
}
}
}
end Rec }

-- RHS size: {terms: 15, types: 14, coercions: 0}
applicationF
applicationF =
\ x_a12W ->
case $sgo3_r577 0# x_a12W of _ { I# ww3_a2iO ->
case $wshowSignedInt 0# ww3_a2iO []
of _ { (# ww5_a2iS, ww6_a2iT #) ->
: ww5_a2iS ww6_a2iT
}
}
...

全部 go函数完全相同(无变量名), application*composition* 相同.所以继续在 Haskell 中创建你自己的 F# 前奏,不应该有任何性能问题。

关于Haskell 性能 : Composition vs Application?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47132554/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com