前回の続き
前回はL-04までやりましたので、今回はL-05から試してみようと思います。第5問目。「リストを逆順にする関数」の作成。OCamlのコードは以下。
let rev list =
let rec aux acc = function
| [] -> acc
| hd :: tl -> aux (hd::acc) tl in
aux [] list
;;
与えられたリストの先頭をaccの先頭に追加していくという形。OCamlでは
::
があって、Lispと同様コンスできるのですが、Rustにはない。そのかわり、Vec
にはinsert
という関数があり、指定したインデックスに値を挿入できるので、これを使ってみようと思います。Rustでは以下のような感じになりました。
fn rev(list: &[i32]) -> Vec<i32> {
let mut result: Vec<i32> = vec![];
fn aux(acc: &mut Vec<i32>, l: &[i32]) {
match l {
[] => {},
[hd, tl @ ..] => {
acc.insert(0, *hd);
aux(acc, tl);
}
}
}
aux(&mut result, list);
result.to_vec()
}
前回同様、関数内関数の形になりました。insert
の部分では常に先頭に挿入するように、インデックスを0としています。続いて第6問目。「回文になっているかどうかを判定する関数」の作成。これは上記の逆順にする関数を使用して以下のような簡単なものになります。
fn is_palindrome(list: &[i32]) -> bool {
list == &*(rev(list))
}
rev
の戻り値の型をVec
にしたので、&*(rev(list))
というややこしい形になってしまいました。このあたり、私のRustの知識、いやプログラミングの知識のなさが露呈してしまっている感が否めませんが、気にせず進みます。第7問目。「ネストされているリストをネストのないリストへ変換する関数」を作成。
OCamlのコードは以下。
type 'a node =
| One of 'a
| Many of 'a node list
let flatten list =
let rec aux acc = function
| [] -> acc
| One x :: tl -> aux (x::acc) tl
| Many l :: tl -> aux (aux acc l) tl in
List.rev (aux [] list)
;;
まずネストされたリストを作成するためにnode
というバリアント型を作成。それをflattenしていきます。Rustでやってみると、
enum Node {
One(i32),
Many(Vec),
}
fn flatten(list: &[Node]) -> Vec<i32> {
let mut result: Vec<i32> = vec![];
fn aux(acc: &mut Vec<i32>, l: &[Node]) {
match l {
[] => {},
[Node::One(x), tl @ ..] => {
acc.insert(0, *x);
aux(acc, tl);
},
[Node::Many(l), tl @ ..] => {
aux(acc, l);
aux(acc, tl);
}
}
}
aux(&mut result, list);
rev(&result).to_vec()
}
Rustではまずenumを使ってOCamlのバリアント型と同様のものを作成し、同様の処理を行っていきます。rev
はこの記事の最初で作った関数を使用しました。まとめ
徐々に怪しいコードとなって参りました。さらに言うならば、今の所i32
しか使っていないという非常に限られた範囲でしか使うことができないコードを書いてしまっています。おそらくすべての型でうまく動作させるためには、もっとコード量が増えることでしょう。ひどいコードですが、皆様のRust学習の足しになれば幸いです。