2016-05-05 16 views
2

​​(または同等のもの)を持つ関数がたくさんあるとします。Actionは合計型であり、各関数は1つのバリエーションだけで実際に動作します。どのようにhaskellパターンマッチを効率的に組み合わせるか

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 

代わりに、複数のcase式(multipleCase)の、単一case式(optimisedCase)が発生する機能(例えば。composedTogether等)を構成する方法はありますか?

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 

これは既に魔法のように自動的に最適化されていますか?

答えて

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にしなければなりませんでした。それ以外の場合、GHCは同等のバージョンを検出しているため、

composedTogether :: Action -> Int -> Int 
composedTogether = optimisedCase 

multipleCase :: Action -> Int -> Int 
multipleCase = optimisedCase 

を取得しました。

私のアドバイスは:これらのマイクロ最適化について忘れて、-O2をオンにして、コンパイラにその仕事をさせてください。

言われているように、オプティマイザができることを過大評価しないでください! :-P問題が発生したら、生成されたコアをチェックします。

+0

オプティマイザが多すぎることがあります。先日、GHCが私が割り当てをしたくないときに必要としなかった共有を得るためにサンクを割り当てないようにする方法を理解するために、私は1時間以上かかりました! – dfeuer

0
optimisedCase :: Action -> Int -> Int 
optimisedCase Reset i = 0 
optimisedCase Increment i = i + 1 
optimisedCase Decrement i = i - 1 

これについて少し考えた後、好適な表記法と非常にクリーン(ケース構文に相当する)

+0

ありがとう、私は好みの表記法を表示するために私の質問を編集します。 –

+1

明確にするために、これは私が探していた答えではありません。私は効率的なコードを得るためにコンビネータ(例えば、関数合成)を使って 'tryReset'、' tryIncrement'と 'tryDecrement'関数を組み合わせる方法を探していました。効率的なコードを手作業で書くのではなく、 –

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

私の次の質問は、これを行う最良の方法ですか?これを行う既存のライブラリは既にありますか?プリズム?

関連する問題