nia*_*hoo 7 optimization haskell
我想知道Haskell中最标准的方法是什么.
第一个明确指出我们需要两个参数(大多数时候).
第二个涉及id第二个子句中的函数call(),因此效率较低,因为在第一个实现中我们可以简单地返回第二个参数.
所以我倾向于认为第一个更好,应该是一个选择:更容易阅读和弄清楚它做什么[1],以及函数调用保存.
但我是Haskell的新手,也许编译器会优化这个额外的调用.
xor :: Bool -> Bool -> Bool
xor True x = not x
xor False x = x
xor True = not
xor False = id
Run Code Online (Sandbox Code Playgroud)
此外,我想知道我是否可以用False那里的通配符替换它们.
那么,Haskell的好习惯是什么呢?也许另一个实现?
[1]我们在那里省略它是一个众所周知的函数,让我们想象它是一个非平凡的函数.
谢谢
Tox*_*ris 14
为了便于阅读,我将尝试避免模式匹配,并使用单个方程定义函数,该方程表达要定义的函数的一些有趣内容.这并不总是可行,但对于这个例子,有很多选择:
xor = (/=)xor a b = a /= bxor a b = not (a == b)xor a b = (a && not b) || (not a && b)xor a b = (a || b) && not (a && b)xor a b = odd (fromEnum a + fromEnum b)Dan*_*her 12
当然,这取决于编译器和传递给编译器的选项.
对于这个特定的例子,如果你编译没有优化,GHC会在你编写代码时生成代码,所以第二个版本包含对idresp 的调用.到not.这比第一个版本的效率略低,第一个版本只包含对以下内容的调用not:
Xors.xor1 :: GHC.Types.Bool -> GHC.Types.Bool -> GHC.Types.Bool
[GblId, Arity=2]
Xors.xor1 =
\ (ds_dkm :: GHC.Types.Bool) (x_aeI :: GHC.Types.Bool) ->
case ds_dkm of _ {
GHC.Types.False -> x_aeI;
GHC.Types.True -> GHC.Classes.not x_aeI
}
Xors.xor2 :: GHC.Types.Bool -> GHC.Types.Bool -> GHC.Types.Bool
[GblId, Arity=1]
Xors.xor2 =
\ (ds_dki :: GHC.Types.Bool) ->
case ds_dki of _ {
GHC.Types.False -> GHC.Base.id @ GHC.Types.Bool;
GHC.Types.True -> GHC.Classes.not
}
Run Code Online (Sandbox Code Playgroud)
(调用仍然在生成的程序集中,但核心更具可读性,所以我只发布它).
但是通过优化,两个函数都编译到同一个核心(从而到同一个机器代码),
Xors.xor2 =
\ (ds_dkf :: GHC.Types.Bool) (eta_B1 :: GHC.Types.Bool) ->
case ds_dkf of _ {
GHC.Types.False -> eta_B1;
GHC.Types.True ->
case eta_B1 of _ {
GHC.Types.False -> GHC.Types.True;
GHC.Types.True -> GHC.Types.False
}
}
Run Code Online (Sandbox Code Playgroud)
GHC eta扩展了第二个版本并内联调用id和not,您获得了纯粹的模式匹配.
第二个等式是使用False还是通配符在任一版本中都没有区别,无论是否有优化.
也许编译器会优化这个额外的调用.
如果你要求它进行优化,在这种简单的情况下,GHC将消除额外的呼叫.
让我们想象它是一个非平凡的功能.
这是一个可能的问题.如果代码非常简单,编译器可能无法消除通过定义函数而引入的所有调用,而不是提供所有参数.GHC相当擅长这样做和内联调用,所以你需要相当多的非平凡性来使GHC无法在编译代码时消除对它所知道的简单函数的调用(它当然不会内联对函数的内联调用)不知道编译相关模块时的实现.
如果是关键代码,请始终检查编译器生成的代码,对于GHC,相关标志是-ddump-simpl在优化后生成核心,并-ddump-asm获取生成的程序集.