假设我有一堆类型为Action -> Int -> Int
(或等效)的函数,其中Action
是和类型,并且每个函数仅对其中一个变体执行实际工作。
data Action = Reset | Increment | Decrement
tryReset :: Action -> Int -> Int
tryReset a i = case a of
Reset -> 0
_ -> i
tryIncrement :: Action -> Int -> Int
tryIncrement a i = case a of
Increment -> i + 1
_ -> i
tryDecrement :: Action -> Int -> Int
tryDecrement a i = case a of
Decrement -> i - 1
_ -> i
有没有办法组合函数(例如像composedTogether
)导致单个案例表达式(optimisedCase
),而不是多个案例表达式(multipleCase
)?
composedTogether :: Action -> Int -> Int
composedTogether a = tryReset a . tryIncrement a . tryDecrement a
optimisedCase :: Action -> Int -> Int
optimisedCase Reset i = 0
optimisedCase Increment i = i + 1
optimisedCase Decrement i = i - 1
multipleCase :: Action -> Int -> Int
multipleCase a i = case a of
Decrement -> i - 1
_ -> case a of
Increment -> i + 1
_ -> case a of
Reset -> 0
_ -> i
或者ghc已经神奇并自动优化了吗?
答案 0 :(得分:4)
不要低估GHC优化者。这是ghc -ddump-simpl -O2
(GHC 7.10.1此处)
composedTogether =
\ (a_aoc :: Action) (eta_B1 :: Int) ->
case a_aoc of _ [Occ=Dead] {
Reset -> Optimization.composedTogether1;
Increment ->
case eta_B1 of _ [Occ=Dead] { GHC.Types.I# x_ayY ->
GHC.Types.I# (GHC.Prim.+# x_ayY 1)
};
Decrement ->
case eta_B1 of _ [Occ=Dead] { GHC.Types.I# x_ayN ->
GHC.Types.I# (GHC.Prim.-# x_ayN 1)
}
}
如您所见,所有内容都已内联。
为此,我必须注释您的optimisedCase
。否则,我得到了
composedTogether :: Action -> Int -> Int
composedTogether = optimisedCase
multipleCase :: Action -> Int -> Int
multipleCase = optimisedCase
因为GHC发现了相同的版本。
我的建议是:忘记这些微优化,打开-O2
,让编译器完成它的工作。
话虽如此,也不要过高估计优化者可以做些什么! :-P如果确实重要,请检查生成的Core。
答案 1 :(得分:0)
optimisedCase :: Action -> Int -> Int
optimisedCase Reset i = 0
optimisedCase Increment i = i + 1
optimisedCase Decrement i = i - 1
是首选符号,更清晰(相当于案例语法)
答案 2 :(得分:0)
Action
版本,我认为这是可能的。
import Data.Monoid (Endo(..))
data Action' a = Action'
{ onReset :: a
, onIncrement :: a
, onDecrement :: a
}
instance Functor Action' where
fmap f a = Action' (f $ onReset a) (f $ onIncrement a) (f $ onDecrement a)
tryReset' :: Action' (Endo Int)
tryReset' = Action' (Endo $ const 0) mempty mempty
tryIncrement' :: Action' (Endo Int)
tryIncrement' = Action' mempty (Endo succ) mempty
tryDecrement' :: Action' (Endo Int)
tryDecrement' = Action' mempty mempty (Endo pred)
composeAction' :: Monoid a => Action' a -> Action' a -> Action' a
composeAction' x y = Action'
(onReset x `mappend` onReset y)
(onIncrement x `mappend` onIncrement y)
(onDecrement x `mappend` onDecrement y)
composedTogether' :: Action' (Endo Int)
composedTogether' = tryReset'
`composeAction'` tryIncrement'
`composeAction'` tryDecrement'
action :: Action' a -> Action -> a
action a Reset = onReset a
action a Increment = onIncrement a
action a Decrement = onDecrement a
doComposedTogether' :: Action -> Int -> Int
doComposedTogether' = action (appEndo <$> composedTogether')
我的下一个问题是:这是最好的方法吗?是否已有现有的库可以执行此操作?棱镜?