《プログラミングの基礎》をReScriptで読む 第14章

[《プログラミングの基礎》](https://www.amazon.co.jp/dp/4781911609/ "Amazonへのリンク")はとても良い本です。
サンプルプログラムをReScriptで書いて、プログラミングの基礎とReScriptを学びます。
# 環境
* macOS Sonoma(バージョン 14.4.1)
* ReScript 11.1.0-rc.8
* @rescript/core 1.2.0
# 第14章
局所関数定義も`{}`になります。
ReScriptではinfix関数をprefix関数にすることは、できないようです。
```ReScript:Ex14_1.res
// 目的：受け取った整数が偶数かどうかを判定する
// is_even : int -> bool
let is_even = (n) => mod(n, 2) == 0

// 目的：受け取ったリストの中から偶数のみを抜き出したリストを返す
// even : int list -> int list
let even = (lst) => List.filter(lst, is_even)

// テスト
Console.log(even(list{}) == list{})
Console.log(even(list{3}) == list{})
Console.log(even(list{2}) == list{2})
Console.log(even(list{2, 1, 6, 4, 7}) == list{2, 6, 4}) 
Console.log(even(list{1, 2, 6, 4, 7}) == list{2, 6, 4})
```
```ReScript:Ex14_2.res
open Ex10_3 // gakusei_t

// gakusei_t list 型のデータの例
let lst1 = list{}
let lst2 = list{{namae: "asai", tensuu: 70, seiseki: "B"}}
let lst3 = list{{namae: "asai", tensuu: 70, seiseki: "B"}, 
            {namae: "kaneko", tensuu: 85, seiseki: "A"}}
let lst4 = list{{namae: "yoshida", tensuu: 80, seiseki: "A"},
            {namae: "asai", tensuu: 70, seiseki: "B"},
            {namae: "kaneko", tensuu: 85, seiseki: "A"}}
 
// 目的：学生の成績が A かどうかを調べる
// is_A : gakusei_t -> bool
let is_A = (gakusei) => switch gakusei { 
| {namae: _, tensuu: _, seiseki: s} => s == "A" 
}

// 目的：学生リスト lst のうち成績が A の人の数を返す
// count_A : gakusei_t list -> int
let count_A = (lst) => List.length(List.filter(lst, is_A))
 
// テスト
Console.log(count_A(lst1) == 0)
Console.log(count_A(lst2) == 0)
Console.log(count_A(lst3) == 1)
Console.log(count_A(lst4) == 2)
```
```ReScript:Ex14_3.res
// 目的：ふたつの文字列をつなげる
// append_string : string -> string -> string
let append_string = (first, rest_result) => first ++ rest_result
 
// 目的：リスト中の文字列をつなげた文字列を返す
// concat : string list -> string
let concat = (lst) => Js.List.foldRight(append_string, lst, "")
 
// テスト
Console.log(concat(list{}) == "")
Console.log(concat(list{"春"}) == "春")
Console.log(concat(list{"春", "夏", "秋", "冬"}) == "春夏秋冬")
```
```ReScript:Ex14_4.res
open Ex10_3 // gakusei_t

// 目的：受け取った学生リスト lst の得点の合計を返す
// add_tokuten : gakusei_t -> int -> int
let add_tokuten = (gakusei, rest_result) => switch gakusei {
| {namae: _, tensuu: t, seiseki: _} => t + rest_result
}

// 目的：受け取った学生リスト lst の得点の合計を返す
// gakusei_sum : gakusei_t list -> int
let gakusei_sum = (lst) => Js.List.foldRight(add_tokuten, lst, 0)
 
// テスト
Console.log(gakusei_sum(Ex14_2.lst1) == 0)
Console.log(gakusei_sum(Ex14_2.lst2) == 70)
Console.log(gakusei_sum(Ex14_2.lst3) == 155)
Console.log(gakusei_sum(Ex14_2.lst4) == 235)
```
```ReScript:Ex14_5.res
// 問題 14.1
// 目的：受け取ったリストの中から偶数のみを抜き出したリストを返す
// even : int list -> int
let even = (lst) => {
  // 目的：受け取った整数が偶数かどうかを判定する *) 
  // is_even : int -> bool
  let is_even = (n) => mod(n, 2) == 0
  List.filter(lst, is_even)
}

// テスト
Console.log(even(list{}) == list{})
Console.log(even(list{3}) == list{})
Console.log(even(list{2}) == list{2})
Console.log(even(list{2, 1, 6, 4, 7}) == list{2, 6, 4})
Console.log(even(list{1, 2, 6, 4, 7}) == list{2, 6, 4})
 

// 問題 14.2
open Ex10_3 // gakusei_t

// 目的：学生リスト lst のうち成績が A の人の数を返す
// count_A : gakusei_t list -> int
let count_A = (lst) => {
  // 目的：学生の成績が A かどうかを調べる
  // is_A : gakusei_t -> bool
  let is_A = (gakusei) => switch gakusei { 
  | {namae: _, tensuu: _, seiseki: s} => s == "A"
  } 
  List.length((List.filter(lst, is_A)))
}

// テスト
Console.log(count_A(Ex14_2.lst1) == 0)
Console.log(count_A(Ex14_2.lst2) == 0)
Console.log(count_A(Ex14_2.lst3) == 1)
Console.log(count_A(Ex14_2.lst4) == 2) 

// 問題 14.3
// 目的：リスト中の文字列をつなげた文字列を返す
// concat : string list -> string
let concat = (lst) => {
  // 目的：ふたつの文字列をつなげる
  // append_string : string -> string -> string
  let append_string = (first, rest_result) => first ++ rest_result
  Js.List.foldRight(append_string, lst, "")
}

// テスト
Console.log(concat(list{}) == "")
Console.log(concat(list{"春"}) == "春")
Console.log(concat(list{"春", "夏", "秋", "冬"}) == "春夏秋冬")
 
// 目的：受け取った学生リスト lst の得点の合計を返す
// gakusei_sum : gakusei_t list -> int *) 
let gakusei_sum = (lst) => { 
  // 目的：受け取った学生リスト lst の得点の合計を返す
  // add_tokuten : gakusei_t -> int -> int
  let add_tokuten = (gakusei, rest_result) => switch gakusei {
  | {namae: _, tensuu: t, seiseki: _} => t + rest_result
  }
  Js.List.foldRight(add_tokuten, lst, 0)
}

// テスト
Console.log(gakusei_sum(Ex14_2.lst1) == 0)
Console.log(gakusei_sum(Ex14_2.lst2) == 70)
Console.log(gakusei_sum(Ex14_2.lst3) == 155)
Console.log(gakusei_sum(Ex14_2.lst4) == 235)
```
```ReScript:Ex14_6.res
open Ex10_3 // gakusei_t

// 目的：学生リスト lst のうち成績が seiseki0 の人の数を返す
// count : gakusei_t list -> string -> int
let count = (lst, seiseki0) => {
  // 目的：学生の成績が A かどうかを調べる
  // is_seiseki0 : gakusei_t -> bool
  let is_seiseki0 = (gakusei) => switch gakusei { 
  | {namae: _, tensuu: _, seiseki: s} => s == seiseki0
  }
  List.length(List.filter(lst, is_seiseki0))
}

// 目的：学生リスト lst のうち成績が A の人の数を返す
// count_A : gakusei_t list -> int
let count_A = (lst) => count(lst, "A")

// テスト
Console.log(count_A(Ex14_2.lst1) == 0)
Console.log(count_A(Ex14_2.lst2) == 0) 
Console.log(count_A(Ex14_2.lst3) == 1)
Console.log(count_A(Ex14_2.lst4) == 2)
Console.log(count(Ex14_2.lst4, "B") == 1)
Console.log(count(Ex14_2.lst4, "C") == 0)
```
```ReScript:Ex14_7.res
open Ex08_7   // ekikan_t
open Ex09_10  // global_ekikan_list
open Ex12_1   // eki_t

// 目的：ふたつの駅の間の距離を求める
// get_ekikan_kyori : string -> string -> ekikan_t list -> float
let rec get_ekikan_kyori = (eki1, eki2, lst) => {
  switch lst {
  | list{} => Float.Constants.positiveInfinity
  | list{{kiten: k, shuten: s, keiyu: _, kyori: r, jikan: _}, ...rest} =>
     if (eki1 == k && eki2 == s) || (eki1 == s && eki2 == k) {r}
     else {get_ekikan_kyori(eki1, eki2, rest)}
  }
}

// 目的：未確定の駅のリスト v を必要に応じて更新したリストを返す
// koushin : eki_t -> eki_t list -> eki_t list
let koushin = (p, v) => { 
// 目的：未確定の駅 q を必要に応じて更新した駅を返す
// koushin1 : eki_t -> eki_t -> eki_t
  let koushin1 = (p, q) => switch (p, q) { 
  | ({namae: pn, saitan_kyori: ps, temae_list: pt}, 
    {namae: qn, saitan_kyori: qs, temae_list: _}) => {
    let kyori = get_ekikan_kyori(pn, qn, global_ekikan_list)
    if kyori == Float.Constants.positiveInfinity {q}
    else if ps +. kyori < qs {{namae: qn, saitan_kyori: ps +. kyori, temae_list: list{qn, ...pt}}}
    else {q}
    }
  }
  let f = (q) => koushin1(p, q) 
  List.map(v, f)
}

// 駅リストの例
let lst = list{Ex13_6.eki1, Ex13_6.eki2, Ex13_6.eki3, Ex13_6.eki4}
 
// テスト
Console.log(koushin(Ex13_6.eki2, list{}) == list{})
Console.log(koushin(Ex13_6.eki2, lst) == 
 list{{namae: "池袋", saitan_kyori: 3.0, temae_list: list{"池袋", "新大塚", "茗荷谷"}}, 
  Ex13_6.eki2, Ex13_6.eki3, Ex13_6.eki4})
```
```ReScript:Ex14_10.res
// 問題 14.1
// 目的：受け取ったリストの中から偶数のみを抜き出したリストを返す
// even : int list -> int
let even = (lst) => List.filter(lst, n => mod(n, 2) == 0)

// テスト
Console.log(even(list{}) == list{})
Console.log(even(list{3}) == list{})
Console.log(even(list{2}) == list{2})
Console.log(even(list{2, 1, 6, 4, 7}) == list{2, 6, 4})
Console.log(even(list{1, 2, 6, 4, 7}) == list{2, 6, 4})
 

// 問題 14.2
open Ex10_3 // gakusei_t

// 目的：学生リスト lst のうち成績が A の人の数を返す
// count_A : gakusei_t list -> int
let count_A = (lst) => {
  List.length((List.filter(lst, gakusei => switch gakusei {
    | {namae: _, tensuu: _, seiseki: s} => s == "A"
  })))
}

// テスト
Console.log(count_A(Ex14_2.lst1) == 0)
Console.log(count_A(Ex14_2.lst2) == 0)
Console.log(count_A(Ex14_2.lst3) == 1)
Console.log(count_A(Ex14_2.lst4) == 2) 

// 問題 14.3
// 目的：リスト中の文字列をつなげた文字列を返す
// concat : string list -> string
let concat = (lst) => Js.List.foldRight((first, rest_result) => first ++ rest_result, lst, "")

// テスト
Console.log(concat(list{}) == "")
Console.log(concat(list{"春"}) == "春")
Console.log(concat(list{"春", "夏", "秋", "冬"}) == "春夏秋冬")
 
// 目的：受け取った学生リスト lst の得点の合計を返す
// gakusei_sum : gakusei_t list -> int *) 
let gakusei_sum = (lst) => { 
  // 目的：受け取った学生リスト lst の得点の合計を返す
  // add_tokuten : gakusei_t -> int -> int
  let add_tokuten = (gakusei, rest_result) => switch gakusei {
  | {namae: _, tensuu: t, seiseki: _} => t + rest_result
  }
  Js.List.foldRight(add_tokuten, lst, 0)
}

// テスト
Console.log(gakusei_sum(Ex14_2.lst1) == 0)
Console.log(gakusei_sum(Ex14_2.lst2) == 70)
Console.log(gakusei_sum(Ex14_2.lst3) == 155)
Console.log(gakusei_sum(Ex14_2.lst4) == 235)
```
```ReScript:Ex14_11
open Ex08_5 // ekimei_t
open Ex12_1 // eki_t

// 駅名リストの例
let ekimei_list = list{ 
  {kanji: "池袋", kana: "いけぶくろ", romaji: "ikebukuro", shozoku: "丸ノ内線"},
  {kanji: "新大塚", kana: "しんおおつか", romaji: "shinotsuka", shozoku: "丸ノ内線"}, 
  {kanji: "茗荷谷", kana: "みょうがだに", romaji: "myogadani", shozoku: "丸ノ内線"}, 
  {kanji: "後楽園", kana: "こうらくえん", romaji: "korakuen", shozoku: "丸ノ内線"},
  {kanji: "本郷三丁目", kana: "ほんごうさんちょうめ", romaji: "hongosanchome", shozoku: "丸ノ内線"},
  {kanji: "御茶ノ水", kana: "おちゃのみず", romaji: "ochanomizu", shozoku: "丸ノ内線"}
}

// 目的：ekimei list から eki list を作る
// make_eki_list : ekimei_t list -> eki_t list
let make_eki_list = (ekimei_list) => List.map(ekimei_list, ekimei => switch ekimei {
  | {kanji: k, kana: _, romaji: _, shozoku: _} => {namae: k, saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}}
})

// テスト
Console.log(make_eki_list(list{}) == list{})
Console.log(make_eki_list(ekimei_list) == list{ 
{namae: "池袋", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}},
{namae: "新大塚", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}},
{namae: "茗荷谷", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}},
{namae: "後楽園", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}},
{namae: "本郷三丁目", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}},
{namae: "御茶ノ水", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}}
})

// 駅リストの例
let eki_list = list{
{namae: "池袋", saitan_kyori: infinity, temae_list: list{}}, 
{namae: "新大塚", saitan_kyori: infinity, temae_list: list{}}, 
{namae: "茗荷谷", saitan_kyori: infinity, temae_list: list{}}, 
{namae: "後楽園", saitan_kyori: infinity, temae_list: list{}}, 
{namae: "本郷三丁目", saitan_kyori: infinity, temae_list: list{}}, 
{namae: "御茶ノ水", saitan_kyori: infinity, temae_list: list{}} 
}

// 目的：eki_t listを初期化する
// shokika : eki_t list -> string -> eki_t list
let shokika = (eki_list, kiten) =>
  List.map(eki_list, eki => switch eki { 
	| {namae: k, saitan_kyori: _, temae_list: _} => 
	    if k == kiten {{namae: k, saitan_kyori: 0., temae_list: list{k}}}
	    else {eki}
  })

// テスト
Console.log(shokika(list{}, "茗荷谷") == list{})
Console.log(shokika(eki_list, "茗荷谷") == list{ 
{namae: "池袋", saitan_kyori:  Float.Constants.positiveInfinity, temae_list: list{}}, 
{namae: "新大塚", saitan_kyori:  Float.Constants.positiveInfinity, temae_list: list{}}, 
{namae: "茗荷谷", saitan_kyori:  0., temae_list: list{"茗荷谷"}},
{namae: "後楽園", saitan_kyori:  Float.Constants.positiveInfinity, temae_list: list{}}, 
{namae: "本郷三丁目", saitan_kyori:  Float.Constants.positiveInfinity, temae_list: list{}}, 
{namae: "御茶ノ水", saitan_kyori:  Float.Constants.positiveInfinity, temae_list: list{}} 
})
```
```ReScript:Ex14_12.res
open Ex08_5 // ekimei_t
open Ex12_1 // eki_t

// 駅名リストの例
let ekimei_list = list{ 
  {kanji: "池袋", kana: "いけぶくろ", romaji: "ikebukuro", shozoku: "丸ノ内線"},
  {kanji: "新大塚", kana: "しんおおつか", romaji: "shinotsuka", shozoku: "丸ノ内線"}, 
  {kanji: "茗荷谷", kana: "みょうがだに", romaji: "myogadani", shozoku: "丸ノ内線"}, 
  {kanji: "後楽園", kana: "こうらくえん", romaji: "korakuen", shozoku: "丸ノ内線"},
  {kanji: "本郷三丁目", kana: "ほんごうさんちょうめ", romaji: "hongosanchome", shozoku: "丸ノ内線"},
  {kanji: "御茶ノ水", kana: "おちゃのみず", romaji: "ochanomizu", shozoku: "丸ノ内線"}
}

// 目的：ekimei list から eki list を作る
// make_initial_eki_list : ekimei_t list -> string -> eki_t list
let make_initial_eki_list = (ekimei_list, kiten) => List.map(ekimei_list, ekimei => switch ekimei {
  | {kanji: k, kana: _, romaji: _, shozoku: _} => 
    if k == kiten {{namae: k, saitan_kyori: 0., temae_list: list{k}}}
    else {{namae: k, saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}}}
  }
)

// テスト
Console.log(make_initial_eki_list(list{}, "茗荷谷") == list{})
Console.log(make_initial_eki_list(ekimei_list, "茗荷谷") == list{ 
{namae: "池袋", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}}, 
{namae: "新大塚", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}}, 
{namae: "茗荷谷", saitan_kyori: 0., temae_list: list{"茗荷谷"}}, 
{namae: "後楽園", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}}, 
{namae: "本郷三丁目", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}}, 
{namae: "御茶ノ水", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}} 
})
```
```ReScript:Ex14_13.res
open Ex08_7   // ekikan_t
open Ex09_10  // global_ekikan_list
open Ex12_1   // eki_t

// 目的：ふたつの駅の間の距離を求める
// get_ekikan_kyori : string -> string -> ekikan_t list -> float
let rec get_ekikan_kyori = (eki1, eki2, lst) => {
  switch lst {
  | list{} => Float.Constants.positiveInfinity
  | list{{kiten: k, shuten: s, keiyu: _, kyori: r, jikan: _}, ...rest} =>
     if (eki1 == k && eki2 == s) || (eki1 == s && eki2 == k) {r}
     else {get_ekikan_kyori(eki1, eki2, rest)}
  }
}

// 目的：未確定の駅のリスト v を必要に応じて更新したリストを返す
// koushin : eki_t -> eki_t list -> eki_t list
let koushin = (p, v) => List.map(v, (q) =>
  switch (p, q) { 
  | ({namae: pn, saitan_kyori: ps, temae_list: pt}, 
    {namae: qn, saitan_kyori: qs, temae_list: _}) => {
    let kyori = get_ekikan_kyori(pn, qn, global_ekikan_list)
    if kyori == Float.Constants.positiveInfinity {q}
    else if ps +. kyori < qs {{namae: qn, saitan_kyori: ps +. kyori, temae_list: list{qn, ...pt}}}
    else {q}
    }
  })

// 駅の例
let eki1 = {namae: "池袋", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}}
let eki2 = {namae: "新大塚", saitan_kyori: 1.2, temae_list: list{"新大塚", "茗荷谷"}}
let eki3 = {namae: "茗荷谷", saitan_kyori: 0., temae_list: list{"茗荷谷"}}
let eki4 = {namae: "後楽園", saitan_kyori: Float.Constants.positiveInfinity, temae_list: list{}}

// 駅リストの例
let lst = list{eki1, eki2, eki3, eki4}
 
// テスト
Console.log(koushin(eki2, list{}) == list{})
/*
Console.log(koushin(eki2, lst) == 
 list{{namae: "池袋", saitan_kyori: 3.0, temae_list: list{"池袋", "新大塚", "茗荷谷"}}, 
  eki2, eki3, eki4})
*/
```
```ReScript:Ex14_14.res
// 目的：リスト中の文字列をつなげた文字列を返す
// concat : string list -> string
let concat = (lst) => Js.List.foldRight((a, b) => a ++ b, lst, "")

//テスト
Console.log(concat(list{}) == "")
Console.log(concat(list{"春"}) == "春")
Console.log(concat(list{"春", "夏", "秋", "冬"}) == "春夏秋冬")
```
```ReScript:Ex14_15.res
// 目的：n から 1 までのリストを作る
// enumerate : int -> int list
let rec enumerate = (n) =>
  if n == 0 {list{}} else {list{n, ...enumerate(n - 1)}}
 
// 目的：1 から受け取った自然数 n までの合計を返す
// one_to_n : int -> int
let one_to_n = (n) =>
  Js.List.foldRight((a, b) => a + b, enumerate(n), 0)

Console.log(one_to_n(0) == 0)
Console.log(one_to_n(1) == 1)
Console.log(one_to_n(2) == 3)
Console.log(one_to_n(10) == 55)
```
```ReScript:Ex14_16.res
// 目的：n から 1 までのリストを作る
// enumerate : int -> int list
let rec enumerate = (n) =>
  if n == 0 {list{}} else {list{n, ...enumerate(n - 1)}}

 // 目的：自然数 n の階乗を求める
// fac : int -> int
let fac = (n) => Js.List.foldRight((a, b) => a * b,  enumerate(n), 1)
 
// テスト
Console.log(fac(0) == 1)
Console.log(fac(1) == 1)
Console.log(fac(2) == 2)
Console.log(fac(3) == 6)
Console.log(fac(4) == 24)
Console.log(fac(10) == 3628800)
```