OCamlと比較してみる
L99問題は以前Common Lispの記事で少し取り上げました。今回はOCamlのL99を元にRustで少しやってみようかと思います。まず第一問目は、「リストの最後の要素を返す関数と作成する」という問題です。OCamlでは以下のようになります。
let rec last = function
| [] -> None
| [x] -> Some x
| _ :: tl -> last tl
;;
OCamlでは再帰関数にはrec
をつけます。上記のコードはfunction
を使ってちょっと省略した書き方になっていますが、以下のコードと同じです。
let rec last list =
match list with
| [] -> None
| [x] -> Some x
| _ :: tl -> last tl
;;
ではRustで同じように作成してみるとどうなるでしょうか?
fn last(list: &[i32]) -> Option<i32> {
match list {
[] => None,
[x] => Some(*x),
[_, tl @ ..] => last(tl),
}
}
こんな感じになるのではないかと思います。第2問目。「リストの最後の2つの要素を返す関数を作成する」という問題。まずはOCamlのコードから
let rec last_two = function
| [] | [_] -> None
| [x; y] -> Some (x, y)
| _ :: tl -> last_two tl
;;
リストが空か要素が一つの場合、2つの要素を返すことができないのでNone
となります。要素が2つの場合は、ぴったしなのでそれを返します。それ以上要素がある場合は再帰的に処理を繰り返します。これをRustでやってみますと
fn last_two(list: &[i32]) -> Option<(i32, i32)> {
match list {
[] | [_] => None,
[x, y] => Some((*x, *y)),
[_, tl @ ..] => last_two(tl)
}
}
第3問目。「k番目の要素を返す関数を作成する」という問題。通常リストは0番から始まりますが、この関数の場合は、1から始めるというものです。OCamlでのコードは以下のようになります。
let rec at k = function
| [] -> None
| hd :: tl -> if k = 1 then Some hd else at (k - 1) tl
;;
リストが空だと当然None
。kが1の場合は最初の要素なので、hd
を返す。再帰的に残りのリストを探していきます。ざっくり言うとリストをどんどん削って行く感じです。tl
があり、かつkが1でないかぎり、kをデクリメントし、削り続け、最終的にkが1になった時点で削るのをやめ、先頭の要素を返す。といった感じでしょうか。説明下手ですみません。頭の中ではイメージできているんですが……Rustでやってみます
fn at(list: &[i32], k: usize) -> Option<i32> {
match list {
[] => None,
[hd, tl @ ..] => {
if k == 1 {
Some(*hd)
} else {
at(tl, k - 1)
}
}
}
}
第4問目。「リストの要素の数を返す関数」の作成。これはlen
を使えばよいわけですが、今回はないものとして考えます。OCamlのコードは以下のようなもの
let length list =
let rec aux n = function
| [] -> n
| _ :: tl -> aux (n + 1) tl in
aux 0 list
;;
これをRustでやってみると
fn length(list: &[i32]) -> usize {
fn aux(n: usize, l: &[i32]) -> usize {
match l {
[] => n,
[_, tl @ ..] => aux(n + 1, tl)
}
}
aux(0, list)
}
関数内関数という形になってしまいました。果たして正解なのだろうか?(動作は正常)