LoginSignup
2

More than 3 years have passed since last update.

たのしい Foldable

Posted at

Foldable がよくわからない人のための3分くっきんぐです!

Foldable って何?

どんなに複雑なデータ構造でも、一列のフラットなデータ構造と同一視できるという性質のことです。

例えば、

data Tree a = Leaf a | Node (Tree a) (Tree a)

      Node
      /  \
  Node    Node
  /  \    /  \
 a    b  c    d

これを、

  Node
  /  \
 a   Node
     /  \
    b   Node
        /  \
       c   Node
           /  \
          d   (EMPTY)

これと同じものとみなしても良いという性質が Foldable。

ちなみに List は何もしなくてもこの形なので、一番分かりやすい Foldable の例です。

foldr って何?

フラットにしたデータ構造の節と末端を好きなものに置き換えるだけの関数です。

data List a = Nil | Cons a (List a)
(:) = Cons

1 : 2 : 3 : Nil
  Cons
  /  \
 1   Cons
     /  \
    2   Cons
        /  \
       3    Nil

foldr :: forall f a b. Foldable f => (a -> b -> b) -> b -> f a -> b
foldr f init = ?データ構造ごとに定義されています

foldr (+) 999

   (+)
  /   \
 1    (+)
     /   \
    2    (+)
        /   \
       3     999

(1 : (2 : (3 : Nil))) ==> (1 + (2 + (3 + 999)))
==> 1005


foldr (-) 456

   (-)
  /   \
 1    (-)
     /   \
    2    (-)
        /   \
       3     456

(1 : (2 : (3 : Nil))) ==> (1 - (2 - (3 - 456)))
==> -454


foldr (append <<< show) "END"

 (append <<< show)
  /     \
 1    (append <<< show)
       /     \
      2    (append <<< show)
            /     \
           3      "END"

(1 : (2 : (3 : Nil))) ==> (show 1 <> (show 2 <> (show 3 <> "END")))
==> "123END"

foldl は?

foldr で節を「右向きの関数合成」に置き換えたときに得られるものです。
実質的には、「逆向きにフラットにしたデータ構造」の節と末端を好きなものに置き換えるだけの関数と考えられます。

a : b : c : Nil
  Cons
  /  \
 a   Cons
     /  \
    b   Cons
        /  \
       c    Nil


foldl :: forall f a b. Foldable f => (b -> a -> b) -> b -> f a -> b
foldl f = flip $ foldr (\left right -> flip f left >>> right) identity

  (flip f _ >>> _)
  /     \
 a    (flip f _ >>> _)
      /     \
     b    (flip f _ >>> _)
          /     \
         c      identity

==> (flip f a >>> (flip f b >>> (flip f c >>> identity)))

==> \init -> init # flip f a # flip f b # flip f c # identity
==> \init -> (((init `f` a) `f` b) `f` c)

          `f`
         /   \
       `f`    c
      /   \
    `f`    b
   /   \
 init   a


foldl (+) 999

          (+)
         /   \
       (+)    3
      /   \
    (+)    2
   /   \
 999    1

(1 : (2 : (3 : Nil))) ==> (((999 + 1) + 2) + 3)
==> 1005


foldl (-) 456

          (-)
         /   \
       (-)    3
      /   \
    (-)    2
   /   \
 456    1

(1 : (2 : (3 : Nil))) ==> (((456 - 1) - 2) - 3)
==> 450

この絵を見れば、foldr と foldl で節を構成する関数の引数順序が入れ替わっている理由も自然に分かりますね?

おわり

  • Foldable なら、データ構造をフラットなものとみなすことができる。
  • foldr は、フラットなデータ構造の節と末端を好きなものに置き換える関数。
  • foldl は foldr の逆向きだけど、foldr で節を関数合成に置き換えたものとも言える。

どのように再帰してるかとか、右から計算とか左から計算とか、そういうむずかしい感じに考えなくても大丈夫。
要は単なるデータ構造の式変換でしかないわけです。かんたんかんたん。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2