如何有效地结合haskell模式匹配

时间:2016-05-05 23:04:47

标签: haskell optimization pattern-matching ghc

假设我有一堆类型为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已经神奇并自动优化了吗?

3 个答案:

答案 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')

我的下一个问题是:这是最好的方法吗?是否已有现有的库可以执行此操作?棱镜?