ありけんファミリー
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- title: 第17章 再帰的なデータ構造 tags: ocaml slideOptions: theme: white slideNumber: 'c/t' center: false transition: 'none' keyboard: true width: '93%' height: '100%' --- <style> /* basic design */ .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p { font-family: 'Meiryo UI', 'Source Sans Pro', Helvetica, sans-serif, 'Helvetica Neue', 'Helvetica', 'Arial', 'Hiragino Sans', 'ヒラギノ角ゴシック', YuGothic, 'Yu Gothic'; text-align: left; line-height: 1.8; letter-spacing: normal; text-shadow: none; word-wrap: break-word; color: #444; } .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5 {font-weight: normal;}, .reveal h6 {font-weight: bold;} .reveal h1, .reveal h2, .reveal h3 {color: #2980b9;} .reveal th {background: #DDD;} .reveal section img {background:none; border:none; box-shadow:none; max-width: 95%; max-height: 95%;} .reveal blockquote {width: 90%; padding: 0.5vw 3.0vw;} .reveal table {margin: 1.0vw auto;} .reveal code {line-height: 1.2;} .reveal p, .reveal li {padding: 0vw; margin: 0vw;} .reveal .box {margin: -0.5vw 1.5vw 2.0vw -1.5vw; padding: 0.5vw 1.5vw 0.5vw 1.5vw; background: #EEE; border-radius: 1.5vw;} /* table design */ .reveal table {background: #f5f5f5;} .reveal th {background: #444; color: #fff;} .reveal td {position: relative; transition: all 300ms;} .reveal tbody:hover td { color: transparent; text-shadow: 0 0 3px #aaa;} .reveal tbody:hover tr:hover td {color: #444; text-shadow: 0 1px 0 #fff;} /* blockquote design */ .reveal blockquote { width: 90%; padding: 0.5vw 0 0.5vw 6.0vw; font-style: italic; background: #f5f5f5; } .reveal blockquote:before{ position: absolute; top: 0.1vw; left: 1vw; content: "\f10d"; font-family: FontAwesome; color: #2980b9; font-size: 3.0vw; } /* font size */ .reveal h1 {font-size: 5.0vw;} .reveal h2 {font-size: 3.0vw;} .reveal h3 {font-size: 2.8vw;} .reveal h4 {font-size: 2.6vw;} .reveal h5 {font-size: 1.8vw;} .reveal h6 {font-size: 2.2vw;} .reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {font-size: 2.2vw;} .reveal code {font-size: 1.6vw;} /* new color */ .red {color: #EE6557;} .blue {color: #16A6B6;} /* split slide */ #right {left: -18.33%; text-align: left; float: left; width: 50%; z-index: -10;} #left {left: 31.25%; text-align: left; float: left; width: 50%; z-index: -10;} </style> <style> /* specific design */ .reveal h1 { margin: 0% -100%; padding: 2% 100% 4% 100%; color: #fff; background: #fffa5e; /* fallback for old browsers */ background: linear-gradient(-45deg, #f7f439, #54ffa4, #23A6D5, #238ed5); background-size: 200% 200%; animation: Gradient 60s ease infinite; } @keyframes Gradient { 0% {background-position: 0% 50%} 50% {background-position: 100% 50%} 100% {background-position: 0% 50%} } .reveal h2 { text-align: center; margin: -5% -50% 2% -50%; padding: 3% 10% 1% 10%; color: #fff; background: #fffa5e; /* fallback for old browsers */ background: -webkit-linear-gradient(to right, #c74646, #fffa5e); /* Chrome 10-25, Safari 5.1-6 */ background: linear-gradient(to right, #c74646, #fffa5e); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ } .reveal h4 { text-align: center; } </style> <!-- --------------------------------------------------------------------------------------- --> # 第17章 <br>再帰的なデータ構造 <br> <br> ### 201803407 有田健一郎 ### 201803451 吉野友貴 --- ## 17.1 バリアント型 8章ではレコードを学びました。レコードはフィールドの集まりで、いろいろな要素がどれも存在するときに使います。 ```ocaml= # {name = "asai"; tensuu = 70; seiseki = 'B'};; - : gakusei_t = {name = "asai"; tensuu = 70; seiseki = 'B'} # ``` --- ## 17.1 バリアント型 一方、要素がすべて存在するのではなく、どれかひとつしか存在しない場合もあります。例えば、運動会で赤組か白組かを表すデータを考えてみましょう。各選手は赤組か白組のどちらか一方にのみ属します。このようなときには、どれかひとつを表す**バリアント型**のデータを使います。 --- ## 17.1 バリアント型 バリアント型のデータを作るときには、まずその型を宣言しなくてはなりません。赤組か白組かを表す型 team_t は type文を使って次のように宣言します。 ```ocaml= # type team_t = Red | White;; type team_t = Red | White # ``` --- ## 17.1 バリアント型 ここで宣言した新しい型 team_t は、赤組を表す Red か、白組を表す White のどちらかを値としてとります。 Red あるいは White と書くだけで team_t 型の値になります。 ```ocaml= # Red;; - : team_t = Red # White;; - : team_t = White # ``` Red や White は team_t 型の値を作るものという意味で **構成子(constructor)** と呼ばれます。 バリアント型の宣言では、使用する構成子を順に縦棒で区切って並べます。型宣言の中に出てくる縦棒"|"は match 文のときと同じく「または」と読みます。 --- ## 17.1 バリアント型 OCaml では大文字で始まるものは構成子は必ず大文字で始まらなくてはならないという規則があります。 ```ocaml= # let A = 3;; Unbound Constructor A # ``` OCaml では大文字で始まるものは構成子、小文字で始まるものは変数という使い分けをしています。 --- ## 17.1 バリアント型 バリアント型のデータを受け取ったとき match 文を使って中身にアクセスします。受け取った種類にしたがった場合分けを行います。 例えば team_t 型のデータに対応して何組かを文字列で返す関数 team_string は次のようになります。 ```ocaml= (* 目的 : 受け取ったチーム名を文字列で返す関数 *) (* team_string : team_t -> string *) # let team_string team = match team with Red -> "赤組" | White -> "白組" ``` --- ## 17.1 バリアント型 リストを match 文を使って場合分けをする時には、空リストのケースとそうでないケースを両方、きちんと書かないと警告が出ました。これはユーザの定義したバリアント型でも同じです。 例えば、次のように Red の場合分けしか書かないと White の場合を忘れているよと警告が出ます。 ```ocaml= (* 目的 : 受け取ったチーム名を文字列で返す関数 *) (* team_string : team_t -> string *) # let team_string team = match team with Red -> "赤組" Warning 8: this pattern-matching is not exhaustive. Here is an example of a case that is not matched: White # val team_string : team_t -> string = <fun> ``` --- ## 17.1 バリアント型 バリアント型の選択肢である構成子には、引数を持たせることができます。構成子に引数を持たせるには of というキーワードを使って、選択肢を「構成子 of 型」という形にします。 例えば、生まれた年を年号で表すバリアント型 nengou_t を考えてみましょう。 ```ocaml= (* 年号を表す型 *) # type nengou_t = Meiji of int (* 明治 *) | Taisho of int (* 大正 *) | Showa of int (* 昭和 *) | Heisei of int (* 平成 *) | Reiwa of int (* 令和 *) ``` ```ocaml= (* 例 : 昭和42年 *) # Showa (42);; - : nengou_t = Showa 42 # ``` --- ## 17.1 バリアント型 当然のことながら、構成子の引数には正しい型の引数を与えなければなりません。また、引数の個数にも注意しましょう。 ```ocaml= # Meiji (true);; This expression has type bool but is here was expected of type int # Taisho;; The constructor Taisho expects 1 argument(s), but is applied here to 0 argument(s) ``` --- ## 17.1 バリアント型 OCaml では、関数呼び出しをする時には、引数は単に並べて書くだけで括弧は使いません。しかし構成子に引数を与えるときには必ず括弧を使うと思っておくと間違いが減ります。引数が整数など単純な型の場合はなくてもよいのですが、引数が組の場合は必ず必要になってくるので、いつでも括弧を使うと思っておくのがよいでしょう。 --- ## 17.1 バリアント型 構成子が引数を持つときも、場合分けには match 文を使います。 例えば、年号を受け取ったら、対応する西暦年を返す関数 toseireki は次のようになります。 ```ocaml= (* 目的 : 年号を受け取ったら対応する西暦年を返す *) (* to_seireki : nengou_t -> int *) # let to_seireki nengou = match nengou with Meiji (n) -> n + 1867 | Taisho (n) -> n + 1911 | Showa (n) -> n + 1925 | Heisei (n) -> n + 1988 | Reiwa (n) -> n + 2018 ``` ```ocaml= # to_seireki (Reiwa (3));; - : int = 2021 # ``` --- ## 問題 17.1 誕生年と現在の年を nengou_t 型の値として受け取ってきたら、年齢を返す関数 nenrei をデザインレシピにしたがって作れ。 ```ocaml= (* 目的 : 誕生年と現在の年を受け取ってたら、年齢を返す *) (* nenrei : nengou_t -> nengou_t -> int *) let nenrei nengou1 nengou2 = 0;; (* テスト *) let test1 = nenrei (Heisei 11) (Reiwa 3) = 22 let test2 = nenrei (Showa 1) (Reiwa 3) = 95 let test3 = nenrei (Reiwa 1) (Reiwa 3) = 2 val test1 : bool = false val test2 : bool = false val test3 : bool = false ``` --- ## 問題 17.1 ```ocaml= (* 目的 : 誕生年と現在の年を受け取ってたら、年齢を返す *) (* nenrei : nengou_t -> nengou_t -> int *) let nenrei nengou1 nengou2 = to_seireki nengou2 - to_seiseki nengou1;; val test1 : bool = true val test2 : bool = true val test3 : bool = true ``` --- ## 問題 17.2 1月から12月までを表す構成子 Jnuary、・・・ December を持つ型 year_t を宣言せよ。構成子には日を示す引数を取るようにせよ。 ```ocaml= type year_t = January of int (* 1月 *) | February of int (* 2月 *) | March of int (* 3月 *) | April of int (* 4月 *) | May of int (* 5月 *) | June of int (* 6月 *) | July of int (* 7月 *) | August of int (* 8月 *) | September of int (* 9月 *) | October of int (* 10月 *) | November of int (* 11月 *) | December of int (* 12月 *) ``` --- ## 問題 17.3 12星座を示す型 seiza_t を宣言せよ。 ```ocaml= type seiza_t = Yagiza (* 12/22 - 1/19 *) | Mizugameza (* 1/20 - 2/18 *) | Uoza (* 2/19 - 3/20 *) | Ohitsuziza (* 3/21 - 4/19 *) | Ousiza (* 4/20 - 5/20 *) | Futagoza (* 5/21 - 6/21 *) | Kaniza (* 6/22 - 7/22 *) | Shishiza (* 7/23 - 8/22 *) | Otomeza (* 8/23 - 9/22 *) | Tenbinza (* 9/23 - 10/23 *) | Sasoriza (* 10/24 - 11/22 *) | Iteza (* 11/23 - 12/21 *) ``` --- ## 問題 17.4 year_t 型の値を受け取ってきたら、 seiza_t 型の星座を返す関数 seiza をデザインレシピにしたがって作れ。 --- ## 問題 17.4 ```ocaml= (* 目的:月と日を受け取ったら星座を返す関数 *) (* seiza : year_t -> seiza_t *) let seiza year = match year with January (n) -> Yagiza | February (n) -> Yagiza | March (n) -> Yagiza | April (n) -> Yagiza | May (n) -> Yagiza | June (n) -> Yagiza | July (n) -> Yagiza | August (n) -> Yagiza | September (n) -> Yagiza | October (n) -> Yagiza | November (n) -> Yagiza | December (n) -> Yagiza ``` --- ## 問題 17.4 ```ocaml= (* テスト *) let test1 = seiza (February (18)) = Mizugameza;; let test2 = seiza (February (22)) = Uoza;; let test3 = seiza (December (1)) = Iteza;; let test4 = seiza (June (25)) = Kaniza;; val test1 : bool = false # val test2 : bool = false # val test3 : bool = false # val test4 : bool = false ``` --- ## 問題 17.4 ```ocaml= let seiza year = match year with January (n) -> if n < 20 then Yagiza else Mizugameza | February (n) -> if n < 19 then Mizugameza else Uoza | March (n) -> if n < 21 then Uoza else Ohitsuziza | April (n) -> if n < 20 then Ohitsuziza else Ousiza | May (n) -> if n < 21 then Ousiza else Futagoza | June (n) -> if n < 22 then Futagoza else Kaniza | July (n) -> if n < 23 then Kaniza else Shishiza | August (n) -> if n < 23 then Shishiza else Otomeza | September (n) -> if n < 23 then Otomeza else Tenbinza | October (n) -> if n < 24 then Tenbinza else Sasoriza | November (n) -> if n < 23 then Sasoriza else Iteza | December (n) -> if n < 22 then Iteza else Yagiza ``` ```ocaml= val test1 : bool = true # val test2 : bool = true # val test3 : bool = true # val test4 : bool = true ``` --- ## 17.2 木 前節でみてきたバリアント型は、いずれも単に選択肢を提供しているだけです。しかし、バリアント型の威力は自己参照を許すところにあります。ここでは代表的な例として **木** を考えてみましょう。 木というのは空の木、葉、あるいは節からなるデータ構造です。 例 図17.1 (p.180) 。 --- ## 17.2 木 **空の木** というのは文字通り空の木です。ここではそれを **Empty** という構成子で表すことにします。 **葉** というのは木の一番先っぽのことで何かひとつデータを持っているものです。ここではそのデータを整数とし、葉は **Leaf (n)** で表すことにします。nが葉に格納されている整数です。 **節** というのは木の幹が分かれているところです。幹が2本に分かれた先はぞれぞれがまた木になっています。ここで自己参照が使われるわけです。節にもひとつ整数のデータを格納することにすると、節は **Node (left, n, right)** と表すことができます。節は、引数として木と整数と木の3つ組を受け取っています。ここでの left と right はそれぞれ分かれた先の木を表しています。 節における子の数が必ずふたつであるような木のことを2分木と呼びます。 --- ## 17.2 木 木の型を tree_t と名付けることにしましょう。 ```ocaml= (* 木を表す型 *) type tree_t = Empty (* 空の木 *) | Leaf of int (* 葉 *) | Node of tree_t * int * tree_t (* 節 *) ``` 最後の行で Node の引数は tree_t * int * tree_t という組になっています。その中に今、定義しようとしている tree_t が入っていてそこで自己参照しているわけです。 --- ## 17.2 木 空の木や葉は直接、作ることができます。 ```ocaml= # Empty;; - : tree_t = Empty # Leaf (3);; - : tree_t = Leaf 3 # Leaf (24);; - : tree_t = Leaf 24 # ``` --- ## 17.2 木 節はすでに作成ずみの木をふたつ使います。例えば、左の木が空で右の木がLeaf (3) で値が7であるような節は次のようになります。 ```ocaml= # Node (Empty, 7, Leaf (3));; - : tree_t = Node (Empty, 7, Leaf 3) # ``` --- ## 17.2 木 一度、木ができると、それを使ってさらに大きな木を作ることができます。例えば、左の木がここで作った節で右の木がLeaf (24) で値が17であるような木は次のようになります。 ```ocaml= # Node (Node (Empty, 7, Leaf (3)), 17, Leaf (24));; - : tree_t = Node (Node (Empty, 7, Leaf 3), 17, Leaf 24) # ``` --- ## 17.2 木 ここで試しに木の定義に自己参照していないケースがなかったらどうなるのかを考えてみましょう。つまり木の定義が次のような定義だったらどうなるかを考えてみます。 ```ocaml= (* 節のみの木を表す型 *) # type tree_t = Node of tree_t * int * tree_t (* 節 *) ``` Ocamlに入力することができるがこの型を持つデータを具体的に作ることはできません。以下略(p.182) --- ## 17.2 木 ここで定義した木の型の再帰構造を明示しておきましょう。 ```ocaml= (* tree は - Empty 空の木、あるいは - Leaf (n) 値が n の葉、あるいは - Node (t1, n, t2) 左の木が t1、値が n、右の木が t2 であるような節 ( t1 と t2 が自己参照のケース ) という形 *) ``` --- ## 17.3 再帰的なデータ構造に対するデザインレシピ 再帰的なデータ構造を扱う関数を作ります。 以下に再帰的なデータ構造特有のことをまとめてデザインレシピの形で示します。 --- ## 17.3 再帰的なデータ構造に対するデザインレシピ **データ定義** <div class="box"> 入力データや出力データの型が再帰的なデータ構造になるときは、まずその型を定義します。その際、自己参照しないケースがあることを確認します。また、どこに自己参照が含まれているかを確認し、それをコメントとして明示しておきます。この時点でデータの例を作成しておくと、後で楽にテストを作ることができるようになります。 </div> **テンプレート** <div class="box"> 入力データが再帰的なデータ構造の場合は、対応する match 文を作成します。match 文の形は、再帰的データ構造の定義と1対1に対応したものになります。特に、自己参照するケースが再帰呼び出しのケースに対応します。そこにはコメントとして再帰呼び出しを書いておきましょう。再帰呼び出しは自己参照する回数だけ書かれます。 </div> --- ## 17.3 再帰的なデータ構造に対するデザインレシピ ここでは木に格納されている整数をすべて加える関数 sum_tree を作ります。まずデータ定義です。 ```ocaml= (* 木を表す型 *) type tree_t = Empty (* 空の木 *) | Leaf of int (* 葉 *) | Node of tree_t * int * tree_t (* 節 *) (* tree は - Empty 空の木、あるいは - Leaf (n) 値が n の葉、あるいは - Node (t1, n, t2) 左の木が t1、値が n、右の木が t2 であるような節 ( t1 と t2 が自己参照のケース ) という形 *) ``` --- ## 17.3 再帰的なデータ構造に対するデザインレシピ 木のデータの例を作る ```ocaml= (* 木の例 *) let tree1 = Empty let tree2 = Leaf (3) let tree3 = Node (tree1, 4, tree2) let tree4 = Node (tree2, 5, tree3) ``` tree4 は ```ocaml= Node (Leaf (3), 5, Node (Empty, 4, Leaf (3))) ``` を表しています。 --- ## 17.3 再帰的なデータ構造に対するデザインレシピ 目的と例 ```ocaml= (* 目的 : tree に含まれる整数をすべて加える *) (* sum_tree : tree_t -> int *) let rec sum_tree tree = 0 (* テスト *) let test1 = sum_tree tree1 = 0 let test2 = sum_tree tree2 = 3 let test3 = sum_tree tree3 = 7 let test4 = sum_tree tree4 = 15 ``` --- ## 17.3 再帰的なデータ構造に対するデザインレシピ テンプレート ```ocaml= (* 目的 : tree に含まれる整数をすべて加える *) (* sum_tree : tree_t -> int *) let rec sum_tree tree = match tree with Empty -> 0 | Leaf (n) -> 0 | Node (t1, n, t2) -> 0 (* sum_tree t1 *) (* sum_tree t2 *) ``` --- ## 17.3 再帰的なデータ構造に対するデザインレシピ 本体 ```ocaml= (* 目的 : tree に含まれる整数をすべて加える *) (* sum_tree : tree_t -> int *) let rec sum_tree tree = match tree with Empty -> 0 | Leaf (n) -> n | Node (t1, n, t2) -> sum_tree t1 + n + sum_tree t2 ``` --- ## 問題17.5~問題17.8 ```ocaml= (* 木を表す型 *) type tree_t = Empty (* 空の木 *) | Leaf of int (* 葉 *) | Node of tree_t * int * tree_t (* 節 *) (* tree は - Empty 空の木、あるいは - Leaf (n) 値が n の葉、あるいは - Node (t1, n, t2) 左の木が t1、値が n、右の木が t2 であるような節 ( t1 と t2 が自己参照のケース ) という形 *) ``` --- ## 問題17.5 tree_t 型の木を受け取ったら、節や葉に入ってる値をすべて2倍にした木を返す関数 tree_double をデザインレシピにしたがって作れ。 ```ocaml= (* 木の例 *) let tree1 = Empty;; let tree2 = Leaf (10);; let tree3 = Leaf (0);; let tree4 = Node (tree1, 2, tree3);; let tree5 = Node (tree2, 5, tree4);; ``` --- ## 問題17.5 目的と例 ```ocaml= (* 目的 : 木を受け取ったら、節や葉に入っている値をすべて2倍にした木を返す *) (* tree_double : tree_t -> tree_t *) let rec tree_double tree = Empty;; (* テスト *) let test1 = tree_double tree1 = Empty;; let test2 = tree_double tree2 = Leaf (20);; let test3 = tree_double tree3 = Leaf (0);; let test4 = tree_double tree4 = Node (Empty, 4, Leaf (0));; let test5 = tree_double tree5 = Node (Leaf (20), 10, Node (Empty, 4, Leaf (0)));; ``` --- ## 問題17.5 テンプレート ```ocaml= (* 目的 : 木を受け取ったら、節や葉に入っている値をすべて2倍にした木を返す *) (* tree_double : tree_t -> tree_t *) let rec tree_double tree = match tree with Empty -> Empty | Leaf (n) -> Empty | Node (t1, n, t2) -> Empty (* tree_double t1 *) (* tree_double t2 *) ``` --- ## 問題17.5 本体 ```ocaml= (* 目的 : 木を受け取ったら、節や葉に入っている値をすべて2倍にした木を返す *) (* tree_double : tree_t -> tree_t *) let rec tree_double tree = match tree with Empty -> Empty | Leaf (n) -> Leaf (n * 2) | Node (t1, n, t2) -> Node (tree_double t1, n * 2, tree_double t2) ``` ```ocaml= # val tree_double : tree_t -> tree_t = <fun> # val test1 : bool = true # val test2 : bool = true # val test3 : bool = true # val test4 : bool = true # val test5 : bool = true ``` --- ## 問題17.6 int -> int 型の関数 f と tree_t 型の木を受け取ったら、節や葉に入っている値すべてに f を適用した木を返す関数 tree_map をデザインレシピにしたがって作れ。 ```ocaml= (* 木の例 *) let tree1 = Empty;; let tree2 = Leaf (10);; let tree3 = Leaf (0);; let tree4 = Node (tree1, 2, tree3);; let tree5 = Node (tree2, 5, tree4);; ``` --- ## 問題17.6 目的と例 ```ocaml= (* 目的 : 関数 f と木を受け取ったら、節や葉に入っているすべてに f を適用した木を返す *) (* tree_map : (f : (int -> int)) -> tree_t -> tree_t *) let f n = n * n;; let rec tree_map f tree = Empty;; (* テスト *) let test1 = tree_map f tree1 = Empty;; let test2 = tree_map f tree2 = Leaf (100);; let test3 = tree_map f tree3 = Leaf (0);; let test4 = tree_map f tree4 = Node (Empty, 4, Leaf (0));; let test5 = tree_map f tree5 = Node (Leaf (100), 25, Node (Empty, 4, Leaf (0)));; ``` --- ## 問題17.6 テンプレート ```ocaml= (* 目的 : 関数 f と木を受け取ったら、節や葉に入っているすべてに f を適用した木を返す *) (* tree_map : (f : (int -> int)) -> tree_t -> tree_t *) let rec tree_map f tree = match tree with Empty -> Empty | Leaf (n) -> Empty | Node (t1, n, t2) -> Empty (* tree_map f t1 *) (* tree_map f t2 *) ``` --- ## 問題17.6 本体 ```ocaml= (* 目的 : 関数 f と木を受け取ったら、節や葉に入っているすべてに f を適用した木を返す *) (* tree_map : (f : (int -> int)) -> tree_t -> tree_t *) let rec tree_map f tree = match tree with Empty -> Empty | Leaf (n) -> Leaf (f n) | Node (t1, n, t2) -> Node (tree_map f t1, f n, tree_map f t2) ``` ```ocaml= # val tree_map : (int -> int) -> tree_t -> tree_t = <fun> val test1 : bool = true # val test2 : bool = true # val test3 : bool = true # val test4 : bool = true # val test5 : bool = true ``` --- ## 問題17.7 tree_t 型の木を受け取ったら、節と葉が合計いくつあるかを返す関数tree_length をデザインレシピにしたがって作れ。 ```ocaml= (* 木の例 *) let tree1 = Empty;; let tree2 = Leaf (10);; let tree3 = Node (tree1, 1, tree2);; let tree4 = Node (tree1, 2, tree3);; let tree5 = Node (tree2, 5, tree4);; ``` --- ## 問題17.7 目的と例 ```ocaml= (* 目的木を受け取ったら、節と葉が合計いくつあるかを返す *) (* tree_length : tree_t -> int *) let rec tree_length tree = 0;; (* テスト *) let test1 = tree_length tree1 = 0;; let test2 = tree_length tree2 = 1;; let test3 = tree_length tree3 = 2;; let test4 = tree_length tree4 = 3;; let test5 = tree_length tree5 = 5;; ``` --- ## 問題17.7 テンプレート ```ocaml= (* 目的木を受け取ったら、節と葉が合計いくつあるかを返す *) (* tree_length : tree_t -> int *) let rec tree_length tree = match tree with Empty -> 0 | Leaf (n) -> 0 | Node (t1, n ,t2) -> 0 (* tree_length t1 *) (* tree_length t2 *) ``` --- ## 問題17.7 本体 ```ocaml= (* 目的木を受け取ったら、節と葉が合計いくつあるかを返す *) (* tree_length : tree_t -> int *) let rec tree_length tree = match tree with Empty -> 0 | Leaf (n) -> 1 | Node (t1, n ,t2) -> tree_length t1 + 1 + tree_length t2 # val tree_length : tree_t -> int = <fun> val test1 : bool = true # val test2 : bool = true # val test3 : bool = true # val test4 : bool = true # val test5 : bool = true ``` --- ## 問題17.8 tree_t 型の木を受け取ったら、木の深さを返す関数 tree_depth をデザインレシピにしたがって作れ。ここで木の深さは根から葉、または空の木に至る最長の道に含まれる節の数と定義される。ふたつの大きい方を返す関数 max を使ってもよい。 ```ocaml= (* 木の例 *) let tree1 = Empty;; let tree2 = Leaf (10);; let tree3 = Node (tree1, 1, tree2);; let tree4 = Node (tree1, 2, tree3);; let tree5 = Node (tree2, 5, tree4);; ``` --- ## 問題17.8 目的と例 ```ocaml= (* 目的 : 木を受け取ったら、木の深さを返す *) (* tree_depth : tree_t -> int *) let rec tree_depth tree = 0;; (* テスト *) let test1 = tree_depth tree1 = 1;; let test2 = tree_depth tree2 = 1;; let test3 = tree_depth tree3 = 2;; let test4 = tree_depth tree4 = 3;; let test5 = tree_depth tree5 = 4;; ``` --- ## 問題17.8 テンプレート ```ocaml= (* 目的 : 木を受け取ったら、木の深さを返す *) (* tree_depth : tree_t -> int *) let rec tree_depth tree = match tree with Empty -> 0 | Leaf (n) -> 0 | Node (t1, n, t2) -> 0 (* tree_depth t1 *) (* tree_depth t2 *) ``` --- ## 問題17.8 本体 ```ocaml= (* 目的 : 木を受け取ったら、木の深さを返す *) (* tree_depth : tree_t -> int *) let rec tree_depth tree = match tree with Empty -> 1 | Leaf (n) -> 1 | Node (t1, n, t2) -> max (tree_depth t1) (tree_depth t2) + 1 # val tree_depth : tree_t -> int = <fun> val test1 : bool = true # val test2 : bool = true # val test3 : bool = true # val test4 : bool = true # val test5 : bool = true ``` --- ## 17.4 2分探索木 木を使った応用として**2分探索木**を考えてみる。 2分探索木は2分木のうちすべての節について以下のふたつの条件が成り立っているもののことである。 <br> <div class="box"> * 節の左側の木に格納されているデータは、どれもその節に格納されているデータよりも小さい。 * 節の右側の木に格納されているデータは、どれもその節に格納されているデータよりも大きい。 </div> <br> このようにデータの間に大小関係があると、その木の中にあるデータの探索を2分探索で高速に行うことができます。 --- ## 17.4 2分探索木(search編) 2分探索を行う関数searchを作成する。 ```ocaml= (* 目的:dataが2分探索木treeに含まれているかを調べる *) (* search : tree_t -> int -> bool *) let rec search tree data = false ``` :::warning :zap:ひとつ目の引数treeは2分探索木の条件を満たす木でなくてはならない。 ::: ```ocaml= (* 2分探索木の例 *) let tree1 = Empty let tree2 = Leaf (3) let tree3 = Node (Leaf (1), 2, Leaf (3)) let tree4 = Node (Empty, 7, Leaf (9)) let tree5 = Node (tree3, 6, tree4) ``` これらの例はいずれも2分探索木の条件を満たしています。 --- ## 17.4 2分探索木(search編テスト) これらを使うと、テストの例をこのように作成できます。 ```ocaml= (* テスト *) let test1 = search tree1 3 = false let test2 = search tree2 3 = true let test3 = search tree2 4 = false let test4 = search tree5 6 = true let test5 = search tree5 2 = true let test6 = search tree5 1 = true let test7 = s2arch tree5 4 = false let test8 = s3arch tree5 7 = true let test9 = s4arch tree5 8 = false ``` --- ## 17.4 2分探索木(search編テンプレート) ```ocaml= (* 目的:dataが2分探索木treeに含まれているかを調べる *) (* search : tree_t -> int -> bool *) let rec search tree data = match tree with Empty -> false | Leaf (n) -> false | Node (t1, n, t2) -> false (* search t1 data *) (* search t2 data *) ``` --- ## 17.4 2分探索木(search編本体) ```ocaml= (* 目的:dataが2分探索木treeに含まれているかを調べる *) (* search : tree_t -> int -> bool *) let rec search tree data = match tree with Empty -> false | Leaf (n) -> data = n | Node (t1, n, t2) -> if data = n then true else if data < n then search t1 data else search t2 data ``` #### 完成♡ --- ## 17.4 2分探索木(search編テスト) ```ocaml= val test1 : bool = true val test2 : bool = true val test3 : bool = true val test4 : bool = true val test5 : bool = true val test6 : bool = true val test7 : bool = true val test8 : bool = true val test9 : bool = true ``` --- ## 17.4 2分探索木(insert_tree編) ##### 2分探索木に新しい要素を追加する関数insert_treeを考える。 ##### 目的と例は次のようになります。 ```ocaml= (* 目的:2分探索木treeにdataを追加した2分探索木を返す *) (* insert_tree : tree_t -> int -> tree_t *) let rec insert_tree tree data = Empty (* テスト *) let test1 = insert_tree Empty 3 = Leaf (3) let test2 = insert_tree (Leaf (3)) 2 = Node (Leaf (2), 3, Empty) let test3 = insert_tree (Leaf (3)) 3 = Leaf (3) let test4 = insert_tree (Leaf (3)) 4 = Node (Empty, 3, Leaf (4)) let test5 = insert_tree tree5 4 = Node (Node (Leaf (1), 2, Node (Empty, 3, Leaf (4))), 6, Node (Empty, 7, Leaf (9))) ``` ##### test3はすでに木に含まれている要素を挿入した場合であり、今回は重複させず、元の木をそのまま返すことにする。 --- ## 17.4 2分探索木(insert_tree編テンプレート) ```ocaml= (* 目的:2分探索木treeにdataを追加した2分探索木を返す *) (* insert_tree : tree_t -> int -> tree_t *) let rec insert_tree tree data = match tree with Empty -> Empty | Leaf (n) -> Empty | Node (t1, n, t2) -> Empty (* insert_tree t1 data *) (* insert_tree t2 data *) ``` --- ## 17.4 2分探索木(insert_tree編本体) ```ocaml= (* 目的:2分探索木treeにdataを追加した2分探索木を返す *) (* insert_tree : tree_t -> int -> tree_t *) let rec insert_tree tree data = match tree with Empty -> Leaf (data) | Leaf (n) -> if data = n then Leaf (n) else if data < n then Node (Leaf (data), n, Empty) else Node (Empty, n, Leaf (data)) | Node (t1, n, t2) -> if data = n then Node (t1, n, t2) else if data < n then Node (insert_tree t1 data, n, t2) else Node (t1, n, insert_tree t2 data) ``` --- ## 17.4 2分探索木(insert_tree編本体) ```ocaml= (* テスト *) let test1 = insert_tree Empty 3 = Leaf (3) let test2 = insert_tree (Leaf (3)) 2 = Node (Leaf (2), 3, Empty) let test3 = insert_tree (Leaf (3)) 3 = Leaf (3) let test4 = insert_tree (Leaf (3)) 4 = Node (Empty, 3, Leaf (4)) let test5 = insert_tree tree5 4 = Node (Node (Leaf (1), 2, Node (Empty, 3, Leaf (4))), 6, Node (Empty, 7, Leaf (9))) ``` ```ocaml= val test1 : bool = true val test2 : bool = true val test3 : bool = true val test4 : bool = true val test5 : bool = true ``` --- ## 17.4 2分探索木(まとめ) このように2分探索木を使った探索や2分探索木への挿入は、木の構造に従った再帰をするだけで簡単に実現可能である。同様の探索や挿入はリストを使っても行うことができるが、探索の**効率**が大きく異なる。 2分探索木は挿入の順番によっては出来上がる木のバランスが悪くなってしまう場合があるが、それでも平均的にはリストを使った探索よりもずっと速くなる。大量のデータに対して探索する必要があるときには2分探索木の使用を考えるといいだろう。 --- ## 17.5 多相型の宣言 ##### これまでに定義した木は、内部に整数しか持つことができない。これは ```ocaml= (* 木を表す型 *) type tree_t = Empty (* 空の木 *) | Leaf of int (* 葉 *) | Node of tree_t * int * tree_t (* 節 *) ``` ##### のように木を表す型 tree_t の定義の中にintが直接、書き込まれてしまっているからである。リストの型が 'a list という多相型で int list にも string list にも使えるようになっていたのと同じように、木の型も多相型になっている方が使える範囲が広がる。多相の型を宣言するには次のようにする。 ```ocaml= (* 多相の木を表す型 *) type 'a tree_t = Empty (* 空の木 *) | Leaf of 'a (* 葉 *) | Node of 'a tree_t * 'a * 'a tree_t (* 節 *) ``` --- ## 17.5 多相型の宣言 この定義を使うと、要素の型が整数ではない木も作ることができるようになる。<br> 例えば<br> Node (Empty, "a", Leaf ("b")) なら string tree_t型になり、 Node (Leaf (false), true, Empty) なら bool tree_t型になる。 --- ## 17.5 多相型の宣言 木の型を多相型に変更しても、これまでにこの章で作成してきたsearchやinsert_treeはそのまま使える。これらの関数はこれまで ```ocaml= # search ;; val search : tree_t -> int -> bool = <fun> # insert_tree ;; - : tree_t -> int -> tree_t = <fun> # ``` という型だったが、多相の木の型を使って同じ関数を定義すると ```ocaml= # search ;; - : 'a tree_t -> 'a -> bool = <fun> # insert_tree ;; - : 'a tree_t -> 'a -> 'a tree_t = <fun> # ``` という型になる。 --- ## 17.5 多相型の宣言 リストと同様、木のようなデータ構造を定義するときには、なるべく多相にしておくと使える範囲が広がる。 先ほど定義したtree_t ```ocaml= (* 多相の木を表す型 *) type 'a tree_t = Empty (* 空の木 *) | Leaf of 'a (* 葉 *) | Node of 'a tree_t * 'a * 'a tree_t (* 節 *) ``` は引数としてひとつしか受け取らなかったが、引数は複数にすることもできる。例えば ```ocaml= (* 多相の木を表す型(引数が複数) *) type ('a, 'b) tree_t = Empty (* 空の木 *) | Leaf of 'a * 'b (* 葉 *) | Node of ('a, 'b) tree_t * 'a * 'b * ('a, 'b) tree_t (* 節 *) ``` とすれば('a, 'b)というふたつの型の組を引数にとることができる。これは、キーの型が'aで値の型が'bであるような **連想木(association tree)** の型と捉えることができる。 --- ## 問題 17.9 多相の木の型を使って17.3節で作った関数sum_treeを定義せよ。この関数の型は何になるか。なぜ、この関数が受け取る木の要素は多相にならないのか。17.3節で作った関数sum_treeを以下に示す。 ```ocaml= (* 木を表す型 *) type tree_t = Empty (* 空の木 *) | Leaf of int (* 葉 *) | Node of tree_t * int * tree_t (* 節 *) (* 目的:treeに含まれる整数をすべて加える *) (* sum_tree : tree_t -> int *) let rec sum_tree tree = match tree with Empty -> 0 | Leaf (n) -> n | Node (t1, n, t2) -> sum_tree t1 + n + sum_tree t2 ``` --- ## 問題 17.9 多相の木の型を使って17.3節で作った関数sum_treeを定義すると以下のようになる。 ```ocaml= (* 多相の木を表す型 *) type 'a tree_t = Empty (* 空の木 *) | Leaf of 'a (* 葉 *) | Node of 'a tree_t * 'a * 'a tree_t (* 節 *) (* 目的:treeに含まれる整数をすべて加える *) (* sum_tree : int tree_t -> int *) let rec sum_tree tree = match tree with Empty -> 0 | Leaf (n) -> n | Node (t1, n, t2) -> sum_tree t1 + n + sum_tree t2 ``` さらに、関数を実行する。 ```ocaml= # sum_tree ;; - : int tree_t -> int = <fun> ``` --- ## 17.6 停止性 p.81の第2段落およびp.191の下半分を各自読んでみてください。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully