モノイドで再帰的に酔う


免責事項:私はmixologistではありません.これはプロのカクテルのアドバイスではありません!
サムHorvathハントブログ.これはFPモデリングの本当にクールな例です.(Lennart Kolmodinはかつてタンゴのダンスステップがモノイドであると書きました.)
まず最初に、私はカクテルレシピが成分の上の自由な通勤モノであると仮定することによって私の無知を証明します:
  • 成分を加える順序は問題ではない.
  • あなたが一緒に2つのカクテルを加えるならば、あなたはもう一つのカクテルを得ます.
  • アイデンティティカクテルは、それの成分がない空のカクテルです.
  • 第二に、私は再帰的なカクテルのレシピでanteをアップしたい.
    それは、DIKU(コペンハーゲン大学)のコンピュータサイエンス学生レビューのスケッチから来ます:Superdrinks (2002)クレジットは、Uffeクリステンセン、uffe friis lichtenberg、ジョナスUSsing、Niels H . Christensen、Torben .mogensen、jΦrgenエルガラードセンは、どちらかを書いたか、スケッチを制定しました.
    スーパードリンクは以下からなる
  • 1パートジン.
  • レモン2パーツ.
  • 3パーツスーパードリンク.
  • 今、スケッチによると、この飲み物を具体化する多くの悪い方法がありますそのような1つは近似を通してあります:1つのパートGin、2つの部分レモンと3つの部分が何であっても、スーパードリンクのあなたの現在の最高の近似です.このプロセスを十分に繰り返すと、徐々に細かいスーパードリンクがあります.
    再帰的に
    superdrinks(n) = 1 × gin
                   + 2 × lemon
                   + 3 × superdrinks (n-1)
    
    例えばsuperdrinks(0) , それは水かもしれない.それはジンかもしれない!
    少し実験する.
    superdrinks(1) = 1 × gin + 2 × lemon + 3 × superdrinks(0)
    
    superdrinks(2) = 1 × gin + 2 × lemon + 3 × superdrinks(1)
                   = 1 × gin
                   + 2 × lemon
                   + 3 × (1 × gin + 2 × lemon + 3 × superdrinks(0))
                   = 4 × gin + 8 × lemon + 9 × superdrinks(0)
    
    各成分の部分の数の関係はclosed form 排除
    superdrinks(n) = (3ⁿ - 1)/2 × gin
                   + (3ⁿ - 1) × lemon
                   + 3ⁿ × superdrinks(0)
    
    (3×3×・・・n個の出現が3であることを認識して閉じた形を見つけることができますⁿ, スーパードリンク(0)より1つのより少ないレモンが常にあります、そして、常にそのジンの量の半分がそれであるということ;または、あなたはそれらを解決することができますrecurrence relation ; または、関数を使用して3つの番号のシリーズを展開することができます.
    > let superdrinks (gin, lemon, super) = (1 + 3*gin, 2 + 3*lemon, 3*super)
    > unzip3 $ take 6 $ iterate superdrinks (0,0,1)
    ([0,1,4,13,40,121],[0,2,8,26,80,242],[1,3,9,27,81,243])
    
    and look them up .)
    schwiftyを得る時間です.
    ジントニックとスーパードリンクを作るには以下の成分が足ります.
    data Ingredient = Gin | Tonic | Lemon
      deriving (Eq, Ord, Show)
    
    カクテルは、成分とその多様性の任意のセットです:
    newtype Cocktail = Cocktail (Map Ingredient Natural)
      deriving (Eq, Ord, Show)
    
    emptyCocktail :: Cocktail
    emptyCocktail = Cocktail Map.empty
    
    便宜上
    parts :: Natural -> Ingredient -> Cocktail
    parts n ingredient =
      Cocktail (Map.singleton ingredient n)
    
    combine :: Cocktail -> Cocktail -> Cocktail
    combine (Cocktail c1) (Cocktail c2) =
      Cocktail (Map.unionWith (+) c1 c2)
    
    このモデリングの結果は以下の通りです.
    > let gintonic = combine (1 `parts` Gin) (2 `parts` Tonic)
    > gintonic == combine gintonic gintonic
    False
    
    これらはカクテルレシピですので、レシピが結局「2部ジン、4部トニック」または「0パートジン」と言わないように、各成分の量を正規化したいです.
    normalize :: Cocktail -> Cocktail
    normalize (Cocktail ingredients) =
      Cocktail . normalize' $ ingredients
      where
        scale = foldr1 gcd (Map.elems ingredients)
        normalize' = Map.map (`div` scale) . Map.filter (/= 0)
    
    (注意してくださいfoldr1 is partial , Haskellの非厳密な意味では、ingredients 内で使用されるので空ですMap.map 0回
    デモンストレーションnormalize :
    > normalize emptyCocktail 
    Cocktail (fromList [])
    
    > normalize (0 `parts` Gin)
    Cocktail (fromList [])
    
    > normalize $ combine (2 `parts` Gin) (4 `parts` Tonic)
    Cocktail (fromList [(Gin,1),(Tonic,2)])
    
    それは特殊化する誘惑されるEq Cocktail 使用するインスタンスnormalize それでc == combine c c すべてのためにc . しかし、あなたが構造的平等のために比較する必要があるならば、あなたがそうすることができないので、私はそれをするのが好きでありません.
    > let (=~) = (==) `on` normalize
    > (1 `parts` Gin) =~ (2 `parts` Gin)
    True
    
    また、正規化を追加することも魅力的でしょうcombine つのカクテルの組み合わせが正規化カクテルです.しかし、このブログ記事はモノイドカクテルについてですのでcombine 構成演算子にとって最良の候補は、そのような選択は実際に連想性の法則を壊すことです.
    > let norm_combine c1 c2 = normalize (combine c1 c2)
    
    > gin1 `norm_combine` (gin1 `norm_combine` tonic1)
    Cocktail (fromList [(Gin,2),(Tonic,1)])
    
    > (gin1 `norm_combine` gin1) `norm_combine` tonic1
    Cocktail (fromList [(Gin,1),(Tonic,1)])
    
    だから私はカクテルのレシピを正規化の概念が好きですが、それはSemigroup Cocktail インスタンスは、おそらくより単純な例を残して、悪い考えであるでしょう
    instance Semigroup Cocktail where
      (<>) = combine
    
    instance Monoid Cocktail where
      mempty = emptyCocktail
    
    スーパードリンクは、次のように表現できます.
    superdrinks :: Natural -> Cocktail -> Cocktail
    superdrinks n base = mconcat
      [ ((3^n - 1) `div` 2) `parts`  Gin
      ,  (3^n - 1)          `parts`  Lemon
      ,  (3^n)              `rounds` base
      ]
    
    rounds :: Natural -> Cocktail -> Cocktail
    rounds n = mconcat . genericReplicate n
    
    最良の近似がベースを使用しているかどうかmempty または、レビュースケッチが示唆するように.n `parts` Gin , 非常に主観的です.第0近似として純粋ジンを用いたスーパードリンクの第5近似
    > normalize $ superdrinks 5 (1 `parts` Gin)
    Cocktail (fromList [(Gin,182),(Lemon,121)])
    
    乾杯!