Upgrade to Pro — share decks privately, control downloads, hide ads and more …

関数型まつり2025登壇資料「関数プログラミングと再帰」

 関数型まつり2025登壇資料「関数プログラミングと再帰」

Avatar for Taison Tsukada

Taison Tsukada

June 14, 2025
Tweet

Other Decks in Programming

Transcript

  1. 17

  2. 木構造の場合 このような単純な木構造があるとする type 'a Tree = | Leaf of 'a

    | Node of 'a Tree * 'a Tree 例: Node (Leaf 1, Node(Leaf 2, Leaf 3)) Node / \ Leaf(1) Node /  \ Leaf(2) Leaf(3) 21
  3. 23

  4. そんなときに継続渡しスタイル(CPS) 「次に何をするか」という関数を再帰呼び出しの引数に渡していく。 ここでいう cont は継続で「残りの計算」を表す。 これは末尾再帰になっていて、スタックには追加されない。 let map f tree

    = let rec mapInner tree cont = match tree with | Leaf x -> Leaf(f x) |> cont | Node (left, right) -> mapInner left (fun leftResult -> mapInner right (fun rightResult -> Node(leftResult, rightResult) |> cont)) mapInner tree id 24
  5. Node (Leaf 1, Leaf 2) を例になにが起こっているのかを紐解いていく。 let map f tree

    = let rec mapInner tree cont = match tree with | Leaf x -> Leaf(f x) |> cont | Node (left, right) -> mapInner left (fun leftResult -> mapInner right (fun rightResult -> Node(leftResult, rightResult) |> cont)) mapInner tree id > map ((*) 2) (Node (Leaf 1, Leaf 2));; val it: int Tree = Node (Leaf 2, Leaf 4) 25
  6. 初回の再帰呼出し 補助関数の引数の状態 tree: Node (Leaf 1, Leaf 2) cont: id

    初回はNodeのパターンに入る。 mapInner を left とそれ以降の計算を引数に再帰呼 出しをする let map f tree = let rec mapInner tree cont = match tree with | Leaf x -> Leaf(f x) |> cont | Node (left, right) -> //このケースに入る mapInner left (fun leftResult -> mapInner right (fun rightResult -> Node(leftResult, rightResult) |> cont)) mapInner tree id 26
  7. 2回目の再帰呼出し 補助関数の引数の状態 tree: Leaf 1 cont: (fun leftResult -> mapInner

    right (fun rightResult -> Node(leftResult, rightResult) |> id 2回目は Leaf のパターン。引数で受け取った関数 f を適用して cont を呼ぶ。 let map f tree = let rec mapInner tree cont = match tree with | Leaf x -> //このケースに入る Leaf(f x) |> cont | Node (left, right) -> mapInner left (fun leftResult -> mapInner right (fun rightResult -> Node(leftResult, rightResult) |> cont)) mapInner tree id 27
  8. cont を呼び出した結果 Leaf 2 |> (fun leftResult -> mapInner right

    (fun rightResult -> Node(leftResult, rightResult) |> id)) mapInner right (fun rightResult -> Node(Leaf 2, rightResult) |> id) 28
  9. 3回目の再帰呼び出し。 補助関数の引数の状態 tree: Leaf 2 cont: (fun rightResult -> Node(Leaf

    2, rightResult) |> id) 今回も Leaf のケースに入る let map f tree = let rec mapInner tree cont = match tree with | Leaf x -> //このケースに入る Leaf(f x) |> cont | Node (left, right) -> mapInner left (fun leftResult -> mapInner right (fun rightResult -> Node(leftResult, rightResult) |> cont)) mapInner tree id 29
  10. contを呼び出した結果 Leaf 4 |> (fun rightResult -> Node(Leaf 2, rightResult)

    |> id) Node(Leaf 2, Leaf 4) |> id Node(Leaf 2, Leaf 4) 30
  11. 改めて全体像 let map f tree = let rec mapInner tree

    cont = match tree with | Leaf x -> Leaf(f x) |> cont | Node (left, right) -> mapInner left (fun leftResult -> mapInner right (fun rightResult -> Node(leftResult, rightResult) |> cont)) mapInner tree id 31
  12. 36

  13. 基準の要素をfirst、それより小さい要素のリストと大きい要素のリストを作り、更にそ れらに対して再帰呼出しをする let rec quickSort lst = match lst with

    | [] -> [] | first :: rest -> let smaller, greater = List.partition ((>=) first) let sourtedSmaller = quickSort smaller let sourtedGreater = quickSort greater failwith "TODO" 39
  14. それぞれの結果を結合させる let rec quickSort lst = match lst with |

    [] -> [] | first :: rest -> let smaller, greater = List.partition ((>=) first) rest quickSort smaller @ [first] @ quickSort greater 40
  15. クイックソートを実装できた let rec quickSort lst = match lst with |

    [] -> [] | first :: rest -> let smaller, greater = List.partition ((>=) first) rest quickSort smaller @ [first] @ quickSort greater 41
  16. 継続渡しスタイル版 let quickSort lst = let rec inner lst cont

    = match lst with | [] -> cont [] | first :: rest -> let smaller, greater = List.partition ((>=) first) rest inner smaller (fun s -> inner greater (fun g -> cont (s @ [first] @ g))) inner lst id 42
  17. 参考 https://fsharpforfunandprofit.com/posts/computation-expressions-conts/ https://practical-scheme.net/docs/cont-j.html 浅井健一(2007) 『プログラミングの基礎』 (Computer Science Library 3) 、サイ

    エンス社. Stuart Halloway, Aaron Bedra(2013) 『プログラミングClojure 第2版』 、オーム社、 (原著:Stuart Halloway and Aaron Bedra, "Programming Clojure, 2nd edition", Pragmatic Bookshelf 2012、翻訳:川合史朗). Michał Płachta(2023) 『なっとく!関数型プログラミング』 、翔泳社、 (原著: Michał Płachta, "Grokking Functional Programming", Manning Publications 2022、翻 訳/監修:株式会社クイープ). 44