SlideShare una empresa de Scribd logo
1 de 59
Descargar para leer sin conexión
12 章 モノイド


HATATANI Shinta(@apstndb)




    November 18, 2012




                            1 / 59
自己紹介



   @apstndb
   千葉工業大学@津田沼 修士課程
   好きな言語は C++
       BoostCon 2011 によると「Haskell は C++ TMP のための擬
       似言語]
       http://boostcon.boost.org/program/sessions#milewski-
       haskell-the-pseudocode-language-for-c-template-
       metaprogramming
       乗るしかないこのビッグウェーブ
       目標は光と闇が両方そなわり最強に見える感じ




                                                              2 / 59
Section 1

12.1 既存の型を新しい型にくるむ




                     3 / 59
11 章のおさらい

 リストをアプリカティブファンクタにする方法は複数

 1. 左辺のリストの関数と右辺のリストの値の全組み合わせ

 ghci> [(+1),(*100),(*5)] <*> [1,2,3]
 [2,3,4,100,200,300,5,10,15]

 2. 左辺の関数を同じ位置にある右辺の値に適用
    既にリストはアプリカティブなので,区別するために ZipList
    a 型 (Control.Applicative) を導入

 ghci> getZipList $ ZipList [(+1),(*100),(*5)]
                 <*> ZipList [1,2,3]
 [2,200,15]


                                                 4 / 59
ZipList はどう実装する?

 別の型を作る必要がある

 1. data を使う

 data ZipList a = ZipList [a]

 値を取り出すにはパターンマッチを使う

 2. data のレコード構文を使う

 data ZipList a = ZipList { getZipList :: [a] }

 リストを取り出す getZipList 関数が手に入る
 ある型の型クラスにするには deriving と instance を使う


                                                  5 / 59
newtype

  3. newtype を使う

  newtype は「1 つの型を取り,それを何かにくるんで別の型に見
  せかける」ことに特化

  実際の ZipList a の定義

  newtype ZipList a = ZipList { getZipList :: [a] }

          Q data の代わりに newtype にしただけだけど何が良
            くなるの?
          A data はコンストラクタに包む時も解く時もランタ
            イムのオーバーヘッドがある
            newtype はコンパイル時のみ別の型として扱われ,
            ランタイムにオーバーヘッドが無い

                                                      6 / 59
newtype 2




            Q 常に data の代わりに newtype を使うのは?
            A newtype は値コンストラクタもフィールドも 1 つ
              data は値コンストラクタもフィールドも複数可

  data Profession = Fighter | Archer | Accountant
  data Race = Human | Elf | Orc | Goblin
  data PlayerCharacter = PlayerCharacter Race Profession




                                                           7 / 59
newtype で deriving
     可能なのは Eq, Ord, Enum, Bounded, Show, Read
         GHC 拡張 GeneralizedNewtypeDeriving で任意の型クラス
     包む型が既にそのインスタンスである

  例えば

  newtype CharList = CharList { getCharList :: [Char] }
      deriving (Eq, Show)

  ghci> CharList "this will be shown!"
  CharList {getCharList = "this will be shown!"}
  ghci> CharList "benny" == CharList "benny"
  True
  ghci> CharList "benny" == CharList "oisters"
  False

                                                          8 / 59
コンストラクタの型


 CharList :: [Char] -> CharList

 [Char](文字列) を受け取って CharList を返す

 getCharList :: CharList -> [Char]

 CharList を受け取って [Char](文字列) を返す
 解釈

 1. 包んだりほどいたり
 2. 2 種類の型間の変換




                                     9 / 59
newtype を使って型クラスのインスタンスを作る (p.260)


 型引数が一致しなくて型クラスのインスタンスにできない場合が
 ある
 Maybe を Functor にするのは簡単

 class Functor f where
     fmap :: (a -> b) -> f a -> f b

 に対して f = Maybe で

 instance Functor Maybe where

 とし,あとは fmap を実装するだけ




                                      10 / 59
タプルを Functor に

          Q タプルを Functor にするには?
            例えば,fmap が第一要素に対して働くようにしたい
            (fmap (+3) (1, 1) が (4, 1) になる)
            タプルのままだと型 (a, b) の a を fmap が変更する
            ことを表すのは難しい.
          A タプルを newtype して,2 つの型引数の順番を入れ
            替えることで Functor のインスタンスにできる

  newtype Pair b a = Pair { getPair :: (a, b) }

  instance Functor (Pair c) where
      fmap f (Pair (x, y)) = Pair (f x, y)

  newtype で作った型にはパターンマッチも使える取り出したタプ
  ルの第一要素 (x) に f を適用してから Pair x y 型に再変換

                                                  11 / 59
この時 fmap の型は

fmap :: (a -> b) -> Pair c a -> Pair c b

class Functor f where
    fmap :: (a -> b) -> f a -> f b

の f = Pair c となる

ghci> getPair $ fmap (*100) (Pair (2, 3))
(200,3)
ghci> getPair $ fmap reverse (Pair ("london calling", 3))
("gnillac nodnol",3)




                                                      12 / 59
newtype と遅延評価 (p.261)
  newtype は既存の型を新しい型に変えるだけ
  これらを Haskell は型としては区別するが,同一の内部表現を使う
  newtype は data より高速なだけでなくパターンマッチがより怠
  惰になる
  Haskell はデフォルトが遅延評価
  undefined を評価するとアウト

  ghci> undefined
  *** Exception: Prelude.undefined

  評価しなければセーフ

  ghci> head [3, 4, 5, undefined, 2, undefined]
  3

                                                  13 / 59
例えば data に対してパターンマッチをする関数を作る

data CoolBool = CoolBool { getCoolBool :: Bool }
helloMe :: CoolBool -> String
helloMe (CoolBool _) = "hello"

ghci> helloMe undefined
"*** Exception: Prelude.undefined

data で定義した型には複数の値コンストラクタがありうる

   (CoolBool _) にマッチするか確認できるところまで評価が
   必要
       undefined を評価してしまう




                                                   14 / 59
data ではなく newtype を使ったら?
  newtype CoolBool = CoolBool { getCoolBool :: Bool }
  helloMe :: CoolBool -> String
  helloMe (CoolBool _) = "hello" -- 変更なし

  ghci> helloMe undefined
  "hello"

  こちらは動く!
  newtype を使った場合はコンストラクタが 1 つだけだと Haskell
  が知っているので,評価をする必要がない
  data と newtype は似ているが異なったメカニズム

     data オリジナルな型を無から作り出す
         パターンマッチは箱から中身を取り出す操作
     newtype 既存の型をもとに,区別される新しい型を作る
         パターンマッチはある型を別の方に直接変換する操作
                                                        15 / 59
type vs. newtype vs. data(p.263)


   type 既存の型に型シノニム (別名) を与える

   type IntList = [Int]

   [Int] 型に IntList という別名を付けただけで,値コンストラク
   タなど使わなくても自由に交換可能

   ghci> ([1,2,3] :: IntList) ++ ([1,2,3] :: [Int])
   [1,2,3,1,2,3]

   主に複雑な型に名前をつけて目的を分かりやすくするために使用




                                                      16 / 59
newtype 既存の型から新しい型を作る
型クラスのインスタンスを作るために使用

newtype CharList = CharList { getCharList :: [Char] }

   CharList と [Char] を++で連結することは不可
   2 つの CharList を++で連結することも不可
       CharList はリストを含んでもリストではない!
   値コンストラクタ名とフィールド名が型の相互変換関数
   型クラスは引き継がない.deriving するか宣言する
   値コンストラクタもフィールドも 1 つの data は newtype に


data 自作の新しいデータ型を作る

   フィールドとコンストラクタを複数持つデータ型を作れる
       リスト, Maybe, 木, etc…
                                                        17 / 59
12.1 のまとめ



    型シノニム (type)
       型シグネチャの整理や型名が体を表すようにしたい
    newtype
       既存の方をある型クラスのインスタンスにしたい
    data
       何かまったく新しいものを作りたい




                                 18 / 59
Section 2

12.2 Monoid 大集合 (p.265)




                          19 / 59
型クラスは同じ振る舞いをする型のインターフェース




   Eq 等号が使える
   Ord 順序が付けられる
   Functor, Applicative …

 新しい型を作る時は欲しい機能の型クラスを実装する




                            20 / 59
こんな型はどうだろう?
    *は 2 つの数を取って掛け算する関数
        1 * xもx * 1もx

 ghci> 4 * 1
 4
 ghci> 1 * 9
 9

    ++は二つのリストを連結する関数
        x ++ [] も [] ++ x も x

 ghci> [1,2,3] ++ []
 [1,2,3]
 ghci> [] ++ [0.5, 2.5]
 [0.5,2.5]

                                21 / 59
共通の性質
    関数の引数は 2 つ
        2 つの引数と返り値の型は同じ
    相手を変えない特殊な値 (単位元) が存在する
    3 つ以上の値をまとめる時,計算する順序を変えても同じ結
    果 (結合的: associativity)

 ghci> (3 * 2) * (8 * 5)
 240
 ghci> 3 * (2 * (8 * 5))
 240
 ghci> "la" ++ ("di" ++ "ga")
 "ladiga"
 ghci> ("la" ++ "di") ++ "ga"
 "ladiga"

 この性質を持つものこそがモノイド!
                                   22 / 59
Monoid 型クラス (p.266)
  Data.Monoid に定義されている Monoid の定義
  class Monoid m where
      mempty :: m
      mappend :: m -> m -> m
      mconcat :: [m] -> m
      mconcat = foldr mappend mempty

     インスタンスは具体型だけ (m は型引数を取らない)
         Functor や Applicative とは違う
     mempty は単位元
     mappend は固有の 2 項演算
         名前は append とついているが,2 つの値から第 3 の値を返す
         関数
     mconcat はモノイドのリストから mappend で 1 つの値を計
     算する関数
         デフォルトは mempty を初期値にした右畳み込み

  実装する必要があるのは mempty と mappend                 23 / 59
モノイド則 (p.267)
  モノイドが満たすべき法則

    単位元
       mempty `mappend` x = x
       x `mappend` mempty = x
    結合的
       (x `mappend` y) `mappend` z = x `mappend` (y
       `mappend` z)

  満たしているかを Haskell は強制しないので,実際に法則を満た
  すのはプログラマの責任
  蛇足 mappend は GHC 7.4 から<>という別名ができた

    fmap に対する<$>


                                                      24 / 59
12.2 のまとめ




  モノイドは

    同じ型の 2 つの値からその型の 1 つの値を作る 2 項演算
    モノイド則を守る必要がある
      単位元
      結合的




                                      25 / 59
Section 3

12.3 モノイドとの遭遇 (p.268)




                        26 / 59
リストはモノイド (p.268)




  instance Monoid [a] where
      mempty = []
      mappend = (++)

  リストは中身の型に関わらず Monoid




                              27 / 59
モノイドとしてリストを使う

 ghci> [1,2,3] `mappend` [4,5,6]
 [1,2,3,4,5,6]
 ghci> ("one" `mappend` "two") `mappend` "tree"
 "onetwotree"
 ghci> "one" `mappend` ("two" `mappend` "tree")
 "onetwotree"
 ghci> "one" `mappend` "two" `mappend` "tree"
 "onetwotree"
 ghci> "pang" `mappend` mempty
 "pang"
 ghci> mconcat [[1, 2], [3, 6], [9]] -- = concat
 [1,2,3,6,9]
 ghci> mempty :: [a] -- リストだと分かるように型注釈
 []


                                                   28 / 59
モノイド則に交換法則はない



 モノイド則は a `mappend` b = b `mappend` a を要求しない
 交換法則は*は満たすが殆どのモノイドは満たさない!

 ghci> "one" `mappend` "two"
 "onetwo"
 ghci> "two" `mappend` "one"
 "twoone"




                                               29 / 59
Product と Sum(p.269)
  数をモノイドにする方法は 2 つある

      *を演算にして 1 を単位元にする
      +を演算にして 0 を単位元にする

  ghci>   0 + 4
  4
  ghci>   5 + 0
  5
  ghci>   (1 + 3) + 5
  9
  ghci>   1 + (3 + 5)
  9

  2 つの方法を使い分けるために newtype がある

                                30 / 59
Product の定義とインスタンス宣言 in Data.Monoid




  newtype Product a = Product { getProduct :: a }
      deriving (Eq, Ord, Read, Show, Bounded)

  instance Num a => Monoid (Product a) where
      mempty = Product 1
      Product x `mappend` Product y = Product (x * y)




                                                        31 / 59
Product を使ってみる


  全ての Num のインスタンス a に対して Product が使える

  ghci> getProduct $ Product   3 `mappend` Product 9
  27
  ghci> getProduct $ Product   3 `mappend` mempty
  3
  ghci> getProduct $ Product   3 `mappend`
                     Product   4 `mappend` Product 2
  24
  ghci> getProduct . mconcat   . map Product $ [3,4,2]
  24




                                                         32 / 59
Sum


  mappend として*ではなく+を使うのが Sum

  ghci> getSum $ Sum 2 `mappend` Sum 9
  11
  ghci> getSum $ mempty `mappend` Sum 3
  3
  ghci> getSum . mconcat . map Sum $ [1,2,3]
  8

      Num a 自身は Monoid のインスタンスではない




                                               33 / 59
Any と All(p.271)


   Bool もモノイドにする方法が 2 通りある

   1. 論理和||をモノイド演算とし False を単位元とする

   newtype Any = Any { getAny :: Bool }
       deriving (Eq, Ord, Read, Show, Bounded)

   instance Monoid Any where
       mempty = Any False
       Any x `mappend` Any y = Any (x || y)




                                                 34 / 59
Any を使ってみる


 1 つでも True があれば結果は True になる

 ghci> getAny $ Any True `mappend` Any False
 True
 ghci> getAny $ mempty `mappend` Any True
 True
 ghci> getAny . mconcat . map Any $
                          [False, False, False, True]
 True
 ghci> getAny $ mempty `mappend` mempty
 False




                                                        35 / 59
All




      2. 論理積&&をモノイド演算とし True を単位元とする

      newtype All = All { getAll :: Bool }

      instance Monoid All where
          mempty = All True
          All x `mappend` All y = All (x && y)




                                                 36 / 59
All を使ってみる

 全てが True の場合のみ結果が True になる

 ghci>   getAll $ mempty `mappend` All True
 True
 ghci>   getAll $ mempty `mappend` All False
 False
 ghci>   getAll . mconcat . map All $ [True, True, True]
 True
 ghci>   getAll . mconcat . map All $ [True, True, False]
 False

    Monoid を使わなくても [Bool] -> Bool の関数 or や and が
    ある



                                                            37 / 59
Ordering モノイド
  ghci> 1 `compare` 2
  LT
  ghci> 2 `compare` 2
  EQ
  ghci> 3 `compare` 2
  GT

  Ordering もモノイドにできる

  instance Monoid Ordering where
      mempty = EQ
      LT `mappend` _ = LT
      EQ `mappend` y = y
      GT `mappend` _ = GT

  左辺の値を優先することは辞書順比較のルールに則っている
                                   38 / 59
Ordering のモノイド則の確認



  ghci>   LT `mappend` GT
  LT
  ghci>   GT `mappend` LT
  GT
  ghci>   mempty `mappend` LT
  LT
  ghci>   mempty `mappend` GT
  GT




                                39 / 59
Ordering を使う


     2 つの文字列を引数にとり Ordering を返す関数
         長さを比較した結果を返す
         長さが同じ時は EQ ではなく文字列の辞書順比較の結果を
         返す
     素直な書き方

  lengthCompare :: String -> String -> Ordering
  lengthCompare x y = let a = length x `compare` length y
                          b = x `compare` y
                      in if a == EQ then b else a




                                                        40 / 59
Ordering を使う 2


     モノイドを活用した書き方

  import Data.Monoid
  lengthCompare :: String -> String -> Ordering
  lengthCompare x y = (length x `compare` length y)
                              `mappend` (x `compare` y)

  ghci> lengthCompare "zen" "ants"
  LT
  ghci> lengthCompare "zen" "ant"
  GT




                                                          41 / 59
Ordering を使う 3
     2 番目に重要な条件として母音の数を比較したい

  import Data.Monoid
  lengthCompare :: String -> String -> Ordering
  lengthCompare x y = (length x `compare` length y)
                     `mappend` (vowels x `compare` vowels y)
                     `mappend` (x `compare` y)
      where vowels = length . filter (`elem` "aeiou")

  ghci> lengthCompare "zen" "anna" -- 1. 長さ
  LT
  ghci> lengthCompare "zen" "ana" -- 2. 母音
  LT
  ghci> lengthCompare "zen" "ann" -- 3. 辞書順
  GT

     比較条件に優先順位を付けるのに便利!
                                                         42 / 59
Maybe モノイド (p.275)
     Maybe a も複数の方法でモノイドになれる

  1. a がモノイドの時に限り Maybe a も Nothing を単位元として
     Just の中身の mappend を使うモノイド
  instance Monoid a => Monoid (Maybe a) where
      mempty = Nothing
      Nothing `mappend` m = m
      m `mappend` Nothing = m
      Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
  ghci> Nothing `mappend` Just "andy"
  Just "andy"
  ghci> Just LT `mappend` Nothing
  Just LT
  ghci> Just (Sum 3) `mappend` Just (Sum 4)
  Just (Sum {getSum = 7})

     失敗するかもしれない計算の返り値を扱うのに便利                               43 / 59
First


   a がモノイドでなければ使えない mappend を使わないなら任意の
   Maybe をモノイドにできる

   2. 第一引数が Just なら第二引数を捨てる

   newtype First a = First { getFirst :: Maybe a }
       deriving (Eq, Ord, Read, Show)

   instance Monoid (First a) where
       mempty = First Nothing
       First (Just x) `mappend` _ = First (Just x)
       First Nothing `mappend` x = x




                                                     44 / 59
First を使ってみる


  ghci> getFirst $ First (Just 'a') `mappend`
                   First (Just 'b')
  Just 'a'
  ghci> getFirst $ First Nothing `mappend`
                   First (Just 'b')
  Just 'b'
  ghci> getFirst $ First (Just 'a') `mappend`
                   First Nothing
  Just 'a'
  ghci> getFirst . mconcat . map First $
                   [Nothing, Just 9, Just 10]
  Just 9



                                                45 / 59
Last


   3. 第 2 引数が Just なら第 1 引数を捨てる

       Data.Monoid に Last a も用意されている

   ghci> getLast . mconcat . map Last $
                   [Nothing, Just 9, Just 10]
   Just 10
   ghci> getLast $ Last (Just "one") `mappend`
                   Last (Just "two")
   Just "two"




                                                 46 / 59
余談 単位元が無かったら?



 モノイドは主に畳み込みに使われる

       Q リストの畳み込みだけなら mempty が無くてもでき
         るのでは?
       A foldr1 のように空リストの場合にエラーを吐くか,
         結果の型が Maybe m になる

 空リストでも使える畳み込み mconcat :: [m] -> m にモノイド
 は必要十分




                                           47 / 59
12.3 のまとめ

    今まで見てきた型にもモノイドがある
    newtype で複数のインスタンスを持つ場合も
      リスト
             ZipList
      Num
             Sum
             Product
      Bool
             Any
             All
      Ordering
      Maybe
             First
             Last

                               48 / 59
Section 4

12.4 モノイドで畳み込む (p.277)




                         49 / 59
Foldable



       リストが畳み込めるのは分かった.
            リスト以外のデータ構造は畳み込めないの?

   それ Foldable でできるよ!

       Functor: 関数で写せるものを表す型クラス
       Foldable: 畳み込みできるものを表す型クラス

   Data.Foldable Foldable 用の foldr, foldl, foldr1, foldl1 を含む




                                                                50 / 59
Foldable を見てみよう

  Prelude のものと区別するために別名を付ける

  import qualified Data.Foldable as F

  ghci> :t foldr
  foldr :: (a -> b -> b) -> b -> [a] -> b
  ghci> :t F.foldr
  F.foldr :: (F.Foldable t) =>
             (a -> b -> b) -> b -> t a -> b

     t = [] で等価
     F.foldr は Foldable なら畳み込みできるので,より一般化



                                              51 / 59
Foldable を使ってみよう
  ghci> foldr (*) 1 [1,2,3]
  6
  ghci> F.foldr (*) 1 [1,2,3]
  6

  リストでは同じ動作

          Q リスト以外のデータ構造は?
          A Maybe も Foldable !

  ghci> F.foldl (+) 2 (Just 9)
  11
  ghci> F.foldr (||) False (Just True)
  True

  Nothing 値が 0 要素,Just 値が 1 要素のリストのように振る舞う
                                             52 / 59
7 章の木構造を Foldable にしてみよう



  data Tree a = EmptyTree | Node a (Tree a) (Tree a)
      deriving (Show)

  を Foldable にすれば畳み込みが可能になる.

     ある型コンストラクタを Foldable のインスタンスにするには
     foldr か foldMap を実装すれば良い
         foldMap 関数の方が簡単




                                                       53 / 59
foldMap とは




  foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m

     a -> m: 中身からモノイドへ変換する関数
     t a: Foldable な構造
     m: 結果のモノイド値
     構造の中身をモノイド値に変換してから畳み込む関数




                                                        54 / 59
Foldable Tree のインスタンス宣言



  instance F.Foldable Tree where
      foldMap f EmptyTree = mempty
      foldMap f (Node x l r) = F.foldMap f l `mappend`
                               f x `mappend`
                               F.foldMap f r

     左右の部分木,ノードの値それぞれがモノイドになるので
     `mappend`できる.
         順番が大事!




                                                         55 / 59
Foldable Tree の使用
  実際に使う場合は,具体的な変換関数を渡す必要は無い

  testTree = Node 5
              (Node 3
                  (Node   1 EmptyTree EmptyTree)
                  (Node   6 EmptyTree EmptyTree)
              )
              (Node 9
                  (Node   8 EmptyTree EmptyTree)
                  (Node   10 EmptyTree EmptyTree)
              )

  リストと同様の畳み込み関数が使用可能に

  ghci> F.foldl (+) 0 testTree
  42
  ghci> F.foldl (*) 1 testTree
  64800
                                                    56 / 59
foldMap を直接使う
  Foldable のインスタンスを定義する以外にも foldMap は役立つ
  木の中に 3 に等しい数があるかを調べる

  ghci> getAny $ F.foldMap (x -> Any $ x == 3) testTree
  True

  モノイド値 Any は True になるものが一つでもあれば畳み込みの
  結果 True になる.

  ghci> getAny $ F.foldMap (x -> Any $ x > 15) testTree
  False

  リストもモノイドなので,任意の Foldable をリストに変換できる.

  ghci> F.foldMap (x -> [x]) testTree
  [1,3,6,5,8,9,10]

                                                           57 / 59
蛇足




     Q なんで foldMap を定義するだけで foldr, foldl, foldr1,
       foldl1 が定義できるんだろう?
     A 本物のプログラマは Haskell を使う
       第 34 回 様々なデータ構造で fold を使えるように
       する Foldable クラス by @shelarcy
       http://itpro.nikkeibp.co.jp/article/COL-
       UMN/20091009/338681/




                                                    58 / 59
12.4 のまとめ




    Foldable を実装すると
       任意の構造で fold ファミリが使えるようになる!
       構造からリストを作るのも簡単




                                    59 / 59

Más contenido relacionado

La actualidad más candente

Rあんなときこんなとき(tokyo r#12)
Rあんなときこんなとき(tokyo r#12)Rあんなときこんなとき(tokyo r#12)
Rあんなときこんなとき(tokyo r#12)Shintaro Fukushima
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14Ryo Suzuki
 
Van laarhoven lens
Van laarhoven lensVan laarhoven lens
Van laarhoven lensNaoki Aoyama
 
C++11概要 ライブラリ編
C++11概要 ライブラリ編C++11概要 ライブラリ編
C++11概要 ライブラリ編egtra
 
Feature Selection with R / in JP
Feature Selection with R / in JPFeature Selection with R / in JP
Feature Selection with R / in JPSercan Ahi
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門Hideyuki Tanaka
 
すごいHaskell楽しく学ぼう 第6章
すごいHaskell楽しく学ぼう 第6章すごいHaskell楽しく学ぼう 第6章
すごいHaskell楽しく学ぼう 第6章aomori ringo
 
Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章Prunus 1350
 
20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swift20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swiftnecocen
 
Java8 Lambda chapter5
Java8 Lambda chapter5Java8 Lambda chapter5
Java8 Lambda chapter5Takinami Kei
 
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜勝成 鈴江
 
Pythonで始めるDropboxAPI
Pythonで始めるDropboxAPIPythonで始めるDropboxAPI
Pythonで始めるDropboxAPIDaisuke Igarashi
 
たのしい高階関数
たのしい高階関数たのしい高階関数
たのしい高階関数Shinichi Kozake
 

La actualidad más candente (20)

Rあんなときこんなとき(tokyo r#12)
Rあんなときこんなとき(tokyo r#12)Rあんなときこんなとき(tokyo r#12)
Rあんなときこんなとき(tokyo r#12)
 
MP in Scala
MP in ScalaMP in Scala
MP in Scala
 
Map
MapMap
Map
 
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14
 
MP in Haskell
MP in HaskellMP in Haskell
MP in Haskell
 
Van laarhoven lens
Van laarhoven lensVan laarhoven lens
Van laarhoven lens
 
Emcjp item21
Emcjp item21Emcjp item21
Emcjp item21
 
C++11概要 ライブラリ編
C++11概要 ライブラリ編C++11概要 ライブラリ編
C++11概要 ライブラリ編
 
 
  
 
 
Feature Selection with R / in JP
Feature Selection with R / in JPFeature Selection with R / in JP
Feature Selection with R / in JP
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
すごいHaskell楽しく学ぼう 第6章
すごいHaskell楽しく学ぼう 第6章すごいHaskell楽しく学ぼう 第6章
すごいHaskell楽しく学ぼう 第6章
 
Applicative functor
Applicative functorApplicative functor
Applicative functor
 
Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章Rで学ぶデータマイニングI 第8章〜第13章
Rで学ぶデータマイニングI 第8章〜第13章
 
20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swift20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swift
 
Java8 Lambda chapter5
Java8 Lambda chapter5Java8 Lambda chapter5
Java8 Lambda chapter5
 
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
 
Pythonで始めるDropboxAPI
Pythonで始めるDropboxAPIPythonで始めるDropboxAPI
Pythonで始めるDropboxAPI
 
たのしい高階関数
たのしい高階関数たのしい高階関数
たのしい高階関数
 

Similar a すごいH 第12章モノイド

並行プログラミングと継続モナド
並行プログラミングと継続モナド並行プログラミングと継続モナド
並行プログラミングと継続モナドKousuke Ruichi
 
Essential Scala 第5章 シーケンス処理
Essential Scala 第5章 シーケンス処理Essential Scala 第5章 シーケンス処理
Essential Scala 第5章 シーケンス処理Takuya Tsuchida
 
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in TokyoGrails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in TokyoTsuyoshi Yamamoto
 
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜Hiromi Ishii
 
モナドハンズオン前座
モナドハンズオン前座モナドハンズオン前座
モナドハンズオン前座bleis tift
 
研究生のためのC++ no.4
研究生のためのC++ no.4研究生のためのC++ no.4
研究生のためのC++ no.4Tomohiro Namba
 
Haskell超初心者勉強会14
Haskell超初心者勉強会14Haskell超初心者勉強会14
Haskell超初心者勉強会14Takashi Kawachi
 
Haskell勉強会 14.1〜14.3 の説明資料
Haskell勉強会 14.1〜14.3 の説明資料Haskell勉強会 14.1〜14.3 の説明資料
Haskell勉強会 14.1〜14.3 の説明資料Etsuji Nakai
 
LastaFluteでKotlinをはじめよう
LastaFluteでKotlinをはじめようLastaFluteでKotlinをはじめよう
LastaFluteでKotlinをはじめようShinsuke Sugaya
 
「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」
「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」
「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」Tsuyoshi Yamamoto
 
Nds meetup8 lt
Nds meetup8 ltNds meetup8 lt
Nds meetup8 ltushiboy
 
Introduction to NumPy & SciPy
Introduction to NumPy & SciPyIntroduction to NumPy & SciPy
Introduction to NumPy & SciPyShiqiao Du
 
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーNode.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーmganeko
 

Similar a すごいH 第12章モノイド (20)

並行プログラミングと継続モナド
並行プログラミングと継続モナド並行プログラミングと継続モナド
並行プログラミングと継続モナド
 
Essential Scala 第5章 シーケンス処理
Essential Scala 第5章 シーケンス処理Essential Scala 第5章 シーケンス処理
Essential Scala 第5章 シーケンス処理
 
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in TokyoGrails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
 
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
 
Project lambda
Project lambdaProject lambda
Project lambda
 
モナドハンズオン前座
モナドハンズオン前座モナドハンズオン前座
モナドハンズオン前座
 
研究生のためのC++ no.4
研究生のためのC++ no.4研究生のためのC++ no.4
研究生のためのC++ no.4
 
Haskell
HaskellHaskell
Haskell
 
Haskell超初心者勉強会14
Haskell超初心者勉強会14Haskell超初心者勉強会14
Haskell超初心者勉強会14
 
Haskell勉強会 14.1〜14.3 の説明資料
Haskell勉強会 14.1〜14.3 の説明資料Haskell勉強会 14.1〜14.3 の説明資料
Haskell勉強会 14.1〜14.3 の説明資料
 
LastaFluteでKotlinをはじめよう
LastaFluteでKotlinをはじめようLastaFluteでKotlinをはじめよう
LastaFluteでKotlinをはじめよう
 
「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」
「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」
「Grails-1.1を斬る!〜Grails-1.1からのチーム開発〜」
 
Nds meetup8 lt
Nds meetup8 ltNds meetup8 lt
Nds meetup8 lt
 
Enshu8
Enshu8Enshu8
Enshu8
 
Thinking in Cats
Thinking in CatsThinking in Cats
Thinking in Cats
 
Introduction to NumPy & SciPy
Introduction to NumPy & SciPyIntroduction to NumPy & SciPy
Introduction to NumPy & SciPy
 
Boost Fusion Library
Boost Fusion LibraryBoost Fusion Library
Boost Fusion Library
 
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーNode.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
 
Ruby test double
Ruby test doubleRuby test double
Ruby test double
 
boost tour 1.48.0 all
boost tour 1.48.0 allboost tour 1.48.0 all
boost tour 1.48.0 all
 

すごいH 第12章モノイド

  • 1. 12 章 モノイド HATATANI Shinta(@apstndb) November 18, 2012 1 / 59
  • 2. 自己紹介 @apstndb 千葉工業大学@津田沼 修士課程 好きな言語は C++ BoostCon 2011 によると「Haskell は C++ TMP のための擬 似言語] http://boostcon.boost.org/program/sessions#milewski- haskell-the-pseudocode-language-for-c-template- metaprogramming 乗るしかないこのビッグウェーブ 目標は光と闇が両方そなわり最強に見える感じ 2 / 59
  • 4. 11 章のおさらい リストをアプリカティブファンクタにする方法は複数 1. 左辺のリストの関数と右辺のリストの値の全組み合わせ ghci> [(+1),(*100),(*5)] <*> [1,2,3] [2,3,4,100,200,300,5,10,15] 2. 左辺の関数を同じ位置にある右辺の値に適用 既にリストはアプリカティブなので,区別するために ZipList a 型 (Control.Applicative) を導入 ghci> getZipList $ ZipList [(+1),(*100),(*5)] <*> ZipList [1,2,3] [2,200,15] 4 / 59
  • 5. ZipList はどう実装する? 別の型を作る必要がある 1. data を使う data ZipList a = ZipList [a] 値を取り出すにはパターンマッチを使う 2. data のレコード構文を使う data ZipList a = ZipList { getZipList :: [a] } リストを取り出す getZipList 関数が手に入る ある型の型クラスにするには deriving と instance を使う 5 / 59
  • 6. newtype 3. newtype を使う newtype は「1 つの型を取り,それを何かにくるんで別の型に見 せかける」ことに特化 実際の ZipList a の定義 newtype ZipList a = ZipList { getZipList :: [a] } Q data の代わりに newtype にしただけだけど何が良 くなるの? A data はコンストラクタに包む時も解く時もランタ イムのオーバーヘッドがある newtype はコンパイル時のみ別の型として扱われ, ランタイムにオーバーヘッドが無い 6 / 59
  • 7. newtype 2 Q 常に data の代わりに newtype を使うのは? A newtype は値コンストラクタもフィールドも 1 つ data は値コンストラクタもフィールドも複数可 data Profession = Fighter | Archer | Accountant data Race = Human | Elf | Orc | Goblin data PlayerCharacter = PlayerCharacter Race Profession 7 / 59
  • 8. newtype で deriving 可能なのは Eq, Ord, Enum, Bounded, Show, Read GHC 拡張 GeneralizedNewtypeDeriving で任意の型クラス 包む型が既にそのインスタンスである 例えば newtype CharList = CharList { getCharList :: [Char] } deriving (Eq, Show) ghci> CharList "this will be shown!" CharList {getCharList = "this will be shown!"} ghci> CharList "benny" == CharList "benny" True ghci> CharList "benny" == CharList "oisters" False 8 / 59
  • 9. コンストラクタの型 CharList :: [Char] -> CharList [Char](文字列) を受け取って CharList を返す getCharList :: CharList -> [Char] CharList を受け取って [Char](文字列) を返す 解釈 1. 包んだりほどいたり 2. 2 種類の型間の変換 9 / 59
  • 10. newtype を使って型クラスのインスタンスを作る (p.260) 型引数が一致しなくて型クラスのインスタンスにできない場合が ある Maybe を Functor にするのは簡単 class Functor f where fmap :: (a -> b) -> f a -> f b に対して f = Maybe で instance Functor Maybe where とし,あとは fmap を実装するだけ 10 / 59
  • 11. タプルを Functor に Q タプルを Functor にするには? 例えば,fmap が第一要素に対して働くようにしたい (fmap (+3) (1, 1) が (4, 1) になる) タプルのままだと型 (a, b) の a を fmap が変更する ことを表すのは難しい. A タプルを newtype して,2 つの型引数の順番を入れ 替えることで Functor のインスタンスにできる newtype Pair b a = Pair { getPair :: (a, b) } instance Functor (Pair c) where fmap f (Pair (x, y)) = Pair (f x, y) newtype で作った型にはパターンマッチも使える取り出したタプ ルの第一要素 (x) に f を適用してから Pair x y 型に再変換 11 / 59
  • 12. この時 fmap の型は fmap :: (a -> b) -> Pair c a -> Pair c b class Functor f where fmap :: (a -> b) -> f a -> f b の f = Pair c となる ghci> getPair $ fmap (*100) (Pair (2, 3)) (200,3) ghci> getPair $ fmap reverse (Pair ("london calling", 3)) ("gnillac nodnol",3) 12 / 59
  • 13. newtype と遅延評価 (p.261) newtype は既存の型を新しい型に変えるだけ これらを Haskell は型としては区別するが,同一の内部表現を使う newtype は data より高速なだけでなくパターンマッチがより怠 惰になる Haskell はデフォルトが遅延評価 undefined を評価するとアウト ghci> undefined *** Exception: Prelude.undefined 評価しなければセーフ ghci> head [3, 4, 5, undefined, 2, undefined] 3 13 / 59
  • 14. 例えば data に対してパターンマッチをする関数を作る data CoolBool = CoolBool { getCoolBool :: Bool } helloMe :: CoolBool -> String helloMe (CoolBool _) = "hello" ghci> helloMe undefined "*** Exception: Prelude.undefined data で定義した型には複数の値コンストラクタがありうる (CoolBool _) にマッチするか確認できるところまで評価が 必要 undefined を評価してしまう 14 / 59
  • 15. data ではなく newtype を使ったら? newtype CoolBool = CoolBool { getCoolBool :: Bool } helloMe :: CoolBool -> String helloMe (CoolBool _) = "hello" -- 変更なし ghci> helloMe undefined "hello" こちらは動く! newtype を使った場合はコンストラクタが 1 つだけだと Haskell が知っているので,評価をする必要がない data と newtype は似ているが異なったメカニズム data オリジナルな型を無から作り出す パターンマッチは箱から中身を取り出す操作 newtype 既存の型をもとに,区別される新しい型を作る パターンマッチはある型を別の方に直接変換する操作 15 / 59
  • 16. type vs. newtype vs. data(p.263) type 既存の型に型シノニム (別名) を与える type IntList = [Int] [Int] 型に IntList という別名を付けただけで,値コンストラク タなど使わなくても自由に交換可能 ghci> ([1,2,3] :: IntList) ++ ([1,2,3] :: [Int]) [1,2,3,1,2,3] 主に複雑な型に名前をつけて目的を分かりやすくするために使用 16 / 59
  • 17. newtype 既存の型から新しい型を作る 型クラスのインスタンスを作るために使用 newtype CharList = CharList { getCharList :: [Char] } CharList と [Char] を++で連結することは不可 2 つの CharList を++で連結することも不可 CharList はリストを含んでもリストではない! 値コンストラクタ名とフィールド名が型の相互変換関数 型クラスは引き継がない.deriving するか宣言する 値コンストラクタもフィールドも 1 つの data は newtype に data 自作の新しいデータ型を作る フィールドとコンストラクタを複数持つデータ型を作れる リスト, Maybe, 木, etc… 17 / 59
  • 18. 12.1 のまとめ 型シノニム (type) 型シグネチャの整理や型名が体を表すようにしたい newtype 既存の方をある型クラスのインスタンスにしたい data 何かまったく新しいものを作りたい 18 / 59
  • 20. 型クラスは同じ振る舞いをする型のインターフェース Eq 等号が使える Ord 順序が付けられる Functor, Applicative … 新しい型を作る時は欲しい機能の型クラスを実装する 20 / 59
  • 21. こんな型はどうだろう? *は 2 つの数を取って掛け算する関数 1 * xもx * 1もx ghci> 4 * 1 4 ghci> 1 * 9 9 ++は二つのリストを連結する関数 x ++ [] も [] ++ x も x ghci> [1,2,3] ++ [] [1,2,3] ghci> [] ++ [0.5, 2.5] [0.5,2.5] 21 / 59
  • 22. 共通の性質 関数の引数は 2 つ 2 つの引数と返り値の型は同じ 相手を変えない特殊な値 (単位元) が存在する 3 つ以上の値をまとめる時,計算する順序を変えても同じ結 果 (結合的: associativity) ghci> (3 * 2) * (8 * 5) 240 ghci> 3 * (2 * (8 * 5)) 240 ghci> "la" ++ ("di" ++ "ga") "ladiga" ghci> ("la" ++ "di") ++ "ga" "ladiga" この性質を持つものこそがモノイド! 22 / 59
  • 23. Monoid 型クラス (p.266) Data.Monoid に定義されている Monoid の定義 class Monoid m where mempty :: m mappend :: m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty インスタンスは具体型だけ (m は型引数を取らない) Functor や Applicative とは違う mempty は単位元 mappend は固有の 2 項演算 名前は append とついているが,2 つの値から第 3 の値を返す 関数 mconcat はモノイドのリストから mappend で 1 つの値を計 算する関数 デフォルトは mempty を初期値にした右畳み込み 実装する必要があるのは mempty と mappend 23 / 59
  • 24. モノイド則 (p.267) モノイドが満たすべき法則 単位元 mempty `mappend` x = x x `mappend` mempty = x 結合的 (x `mappend` y) `mappend` z = x `mappend` (y `mappend` z) 満たしているかを Haskell は強制しないので,実際に法則を満た すのはプログラマの責任 蛇足 mappend は GHC 7.4 から<>という別名ができた fmap に対する<$> 24 / 59
  • 25. 12.2 のまとめ モノイドは 同じ型の 2 つの値からその型の 1 つの値を作る 2 項演算 モノイド則を守る必要がある 単位元 結合的 25 / 59
  • 27. リストはモノイド (p.268) instance Monoid [a] where mempty = [] mappend = (++) リストは中身の型に関わらず Monoid 27 / 59
  • 28. モノイドとしてリストを使う ghci> [1,2,3] `mappend` [4,5,6] [1,2,3,4,5,6] ghci> ("one" `mappend` "two") `mappend` "tree" "onetwotree" ghci> "one" `mappend` ("two" `mappend` "tree") "onetwotree" ghci> "one" `mappend` "two" `mappend` "tree" "onetwotree" ghci> "pang" `mappend` mempty "pang" ghci> mconcat [[1, 2], [3, 6], [9]] -- = concat [1,2,3,6,9] ghci> mempty :: [a] -- リストだと分かるように型注釈 [] 28 / 59
  • 29. モノイド則に交換法則はない モノイド則は a `mappend` b = b `mappend` a を要求しない 交換法則は*は満たすが殆どのモノイドは満たさない! ghci> "one" `mappend` "two" "onetwo" ghci> "two" `mappend` "one" "twoone" 29 / 59
  • 30. Product と Sum(p.269) 数をモノイドにする方法は 2 つある *を演算にして 1 を単位元にする +を演算にして 0 を単位元にする ghci> 0 + 4 4 ghci> 5 + 0 5 ghci> (1 + 3) + 5 9 ghci> 1 + (3 + 5) 9 2 つの方法を使い分けるために newtype がある 30 / 59
  • 31. Product の定義とインスタンス宣言 in Data.Monoid newtype Product a = Product { getProduct :: a } deriving (Eq, Ord, Read, Show, Bounded) instance Num a => Monoid (Product a) where mempty = Product 1 Product x `mappend` Product y = Product (x * y) 31 / 59
  • 32. Product を使ってみる 全ての Num のインスタンス a に対して Product が使える ghci> getProduct $ Product 3 `mappend` Product 9 27 ghci> getProduct $ Product 3 `mappend` mempty 3 ghci> getProduct $ Product 3 `mappend` Product 4 `mappend` Product 2 24 ghci> getProduct . mconcat . map Product $ [3,4,2] 24 32 / 59
  • 33. Sum mappend として*ではなく+を使うのが Sum ghci> getSum $ Sum 2 `mappend` Sum 9 11 ghci> getSum $ mempty `mappend` Sum 3 3 ghci> getSum . mconcat . map Sum $ [1,2,3] 8 Num a 自身は Monoid のインスタンスではない 33 / 59
  • 34. Any と All(p.271) Bool もモノイドにする方法が 2 通りある 1. 論理和||をモノイド演算とし False を単位元とする newtype Any = Any { getAny :: Bool } deriving (Eq, Ord, Read, Show, Bounded) instance Monoid Any where mempty = Any False Any x `mappend` Any y = Any (x || y) 34 / 59
  • 35. Any を使ってみる 1 つでも True があれば結果は True になる ghci> getAny $ Any True `mappend` Any False True ghci> getAny $ mempty `mappend` Any True True ghci> getAny . mconcat . map Any $ [False, False, False, True] True ghci> getAny $ mempty `mappend` mempty False 35 / 59
  • 36. All 2. 論理積&&をモノイド演算とし True を単位元とする newtype All = All { getAll :: Bool } instance Monoid All where mempty = All True All x `mappend` All y = All (x && y) 36 / 59
  • 37. All を使ってみる 全てが True の場合のみ結果が True になる ghci> getAll $ mempty `mappend` All True True ghci> getAll $ mempty `mappend` All False False ghci> getAll . mconcat . map All $ [True, True, True] True ghci> getAll . mconcat . map All $ [True, True, False] False Monoid を使わなくても [Bool] -> Bool の関数 or や and が ある 37 / 59
  • 38. Ordering モノイド ghci> 1 `compare` 2 LT ghci> 2 `compare` 2 EQ ghci> 3 `compare` 2 GT Ordering もモノイドにできる instance Monoid Ordering where mempty = EQ LT `mappend` _ = LT EQ `mappend` y = y GT `mappend` _ = GT 左辺の値を優先することは辞書順比較のルールに則っている 38 / 59
  • 39. Ordering のモノイド則の確認 ghci> LT `mappend` GT LT ghci> GT `mappend` LT GT ghci> mempty `mappend` LT LT ghci> mempty `mappend` GT GT 39 / 59
  • 40. Ordering を使う 2 つの文字列を引数にとり Ordering を返す関数 長さを比較した結果を返す 長さが同じ時は EQ ではなく文字列の辞書順比較の結果を 返す 素直な書き方 lengthCompare :: String -> String -> Ordering lengthCompare x y = let a = length x `compare` length y b = x `compare` y in if a == EQ then b else a 40 / 59
  • 41. Ordering を使う 2 モノイドを活用した書き方 import Data.Monoid lengthCompare :: String -> String -> Ordering lengthCompare x y = (length x `compare` length y) `mappend` (x `compare` y) ghci> lengthCompare "zen" "ants" LT ghci> lengthCompare "zen" "ant" GT 41 / 59
  • 42. Ordering を使う 3 2 番目に重要な条件として母音の数を比較したい import Data.Monoid lengthCompare :: String -> String -> Ordering lengthCompare x y = (length x `compare` length y) `mappend` (vowels x `compare` vowels y) `mappend` (x `compare` y) where vowels = length . filter (`elem` "aeiou") ghci> lengthCompare "zen" "anna" -- 1. 長さ LT ghci> lengthCompare "zen" "ana" -- 2. 母音 LT ghci> lengthCompare "zen" "ann" -- 3. 辞書順 GT 比較条件に優先順位を付けるのに便利! 42 / 59
  • 43. Maybe モノイド (p.275) Maybe a も複数の方法でモノイドになれる 1. a がモノイドの時に限り Maybe a も Nothing を単位元として Just の中身の mappend を使うモノイド instance Monoid a => Monoid (Maybe a) where mempty = Nothing Nothing `mappend` m = m m `mappend` Nothing = m Just m1 `mappend` Just m2 = Just (m1 `mappend` m2) ghci> Nothing `mappend` Just "andy" Just "andy" ghci> Just LT `mappend` Nothing Just LT ghci> Just (Sum 3) `mappend` Just (Sum 4) Just (Sum {getSum = 7}) 失敗するかもしれない計算の返り値を扱うのに便利 43 / 59
  • 44. First a がモノイドでなければ使えない mappend を使わないなら任意の Maybe をモノイドにできる 2. 第一引数が Just なら第二引数を捨てる newtype First a = First { getFirst :: Maybe a } deriving (Eq, Ord, Read, Show) instance Monoid (First a) where mempty = First Nothing First (Just x) `mappend` _ = First (Just x) First Nothing `mappend` x = x 44 / 59
  • 45. First を使ってみる ghci> getFirst $ First (Just 'a') `mappend` First (Just 'b') Just 'a' ghci> getFirst $ First Nothing `mappend` First (Just 'b') Just 'b' ghci> getFirst $ First (Just 'a') `mappend` First Nothing Just 'a' ghci> getFirst . mconcat . map First $ [Nothing, Just 9, Just 10] Just 9 45 / 59
  • 46. Last 3. 第 2 引数が Just なら第 1 引数を捨てる Data.Monoid に Last a も用意されている ghci> getLast . mconcat . map Last $ [Nothing, Just 9, Just 10] Just 10 ghci> getLast $ Last (Just "one") `mappend` Last (Just "two") Just "two" 46 / 59
  • 47. 余談 単位元が無かったら? モノイドは主に畳み込みに使われる Q リストの畳み込みだけなら mempty が無くてもでき るのでは? A foldr1 のように空リストの場合にエラーを吐くか, 結果の型が Maybe m になる 空リストでも使える畳み込み mconcat :: [m] -> m にモノイド は必要十分 47 / 59
  • 48. 12.3 のまとめ 今まで見てきた型にもモノイドがある newtype で複数のインスタンスを持つ場合も リスト ZipList Num Sum Product Bool Any All Ordering Maybe First Last 48 / 59
  • 50. Foldable リストが畳み込めるのは分かった. リスト以外のデータ構造は畳み込めないの? それ Foldable でできるよ! Functor: 関数で写せるものを表す型クラス Foldable: 畳み込みできるものを表す型クラス Data.Foldable Foldable 用の foldr, foldl, foldr1, foldl1 を含む 50 / 59
  • 51. Foldable を見てみよう Prelude のものと区別するために別名を付ける import qualified Data.Foldable as F ghci> :t foldr foldr :: (a -> b -> b) -> b -> [a] -> b ghci> :t F.foldr F.foldr :: (F.Foldable t) => (a -> b -> b) -> b -> t a -> b t = [] で等価 F.foldr は Foldable なら畳み込みできるので,より一般化 51 / 59
  • 52. Foldable を使ってみよう ghci> foldr (*) 1 [1,2,3] 6 ghci> F.foldr (*) 1 [1,2,3] 6 リストでは同じ動作 Q リスト以外のデータ構造は? A Maybe も Foldable ! ghci> F.foldl (+) 2 (Just 9) 11 ghci> F.foldr (||) False (Just True) True Nothing 値が 0 要素,Just 値が 1 要素のリストのように振る舞う 52 / 59
  • 53. 7 章の木構造を Foldable にしてみよう data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show) を Foldable にすれば畳み込みが可能になる. ある型コンストラクタを Foldable のインスタンスにするには foldr か foldMap を実装すれば良い foldMap 関数の方が簡単 53 / 59
  • 54. foldMap とは foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m a -> m: 中身からモノイドへ変換する関数 t a: Foldable な構造 m: 結果のモノイド値 構造の中身をモノイド値に変換してから畳み込む関数 54 / 59
  • 55. Foldable Tree のインスタンス宣言 instance F.Foldable Tree where foldMap f EmptyTree = mempty foldMap f (Node x l r) = F.foldMap f l `mappend` f x `mappend` F.foldMap f r 左右の部分木,ノードの値それぞれがモノイドになるので `mappend`できる. 順番が大事! 55 / 59
  • 56. Foldable Tree の使用 実際に使う場合は,具体的な変換関数を渡す必要は無い testTree = Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (Node 6 EmptyTree EmptyTree) ) (Node 9 (Node 8 EmptyTree EmptyTree) (Node 10 EmptyTree EmptyTree) ) リストと同様の畳み込み関数が使用可能に ghci> F.foldl (+) 0 testTree 42 ghci> F.foldl (*) 1 testTree 64800 56 / 59
  • 57. foldMap を直接使う Foldable のインスタンスを定義する以外にも foldMap は役立つ 木の中に 3 に等しい数があるかを調べる ghci> getAny $ F.foldMap (x -> Any $ x == 3) testTree True モノイド値 Any は True になるものが一つでもあれば畳み込みの 結果 True になる. ghci> getAny $ F.foldMap (x -> Any $ x > 15) testTree False リストもモノイドなので,任意の Foldable をリストに変換できる. ghci> F.foldMap (x -> [x]) testTree [1,3,6,5,8,9,10] 57 / 59
  • 58. 蛇足 Q なんで foldMap を定義するだけで foldr, foldl, foldr1, foldl1 が定義できるんだろう? A 本物のプログラマは Haskell を使う 第 34 回 様々なデータ構造で fold を使えるように する Foldable クラス by @shelarcy http://itpro.nikkeibp.co.jp/article/COL- UMN/20091009/338681/ 58 / 59
  • 59. 12.4 のまとめ Foldable を実装すると 任意の構造で fold ファミリが使えるようになる! 構造からリストを作るのも簡単 59 / 59