2019/08/18

Rustでcar/cdr

Rustでcar/cdr的なもの

Rustでcar/cdr的なものは無いか?

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    let (hd, tl) = v.split_first().unwrap();
    assert_eq!(hd, &1);
    assert_eq!(tl, &[2, 3, 4, 5]);
}
split_firstは、Option<(&T, &[T])>を返します。これを利用すればcar/cdr関数と同じように処理できます。split_first_mutというものもあり、こちらはミュータブルな参照を返します。car/cdrという名前で使いたい場合は以下のような関数を作成すればいいかと思います。



fn car(v: &[i32]) -> &i32 {
    let (hd, _) = v.split_first().unwrap();
    hd
}

fn cdr(v: &[i32]) -> &[i32] {
    let (_, tl) = v.split_first().unwrap();
    tl
}
または以下のような形も良いかもしれません。

fn car(v: &[i32]) -> &i32 {
    match v.split_first() {
        Some((hd, _)) => hd,
        None => panic!("TYPE-ERROR")
    }
}
fn cdr(v: &[i32]) -> &[i32] {
    match v.split_first() {
        Some((_, tl)) => tl,
        None => panic!("TYPE-ERROR")
    }
}
このsplit_first()を用いて繰り返し処理もできます。

fn my_for(v: &[i32]) {
    if let Some((hd, tl)) = v.split_first() {
        println!("{}", hd);
        my_for(tl);
    };
}

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    my_for(&v);
}

----
結果
----
1
2
3
4
5
上記のコードだと、split_firstがSomeの場合だけ処理されるので、Noneになった時点で、処理はストップします。
Rustはちゃんとかゆいところに手が届くように設計されている。
さらに上記のmy_forにひと手間加えると以下のようにごっそり中身を変えることも出来ます。

fn my_for(v: &mut [i32], f: fn(i32) -> i32) {
    if let Some((hd, tl)) = v.split_first_mut() {
        *hd = f(*hd);
        my_for(tl, f);
    };
}

fn main() {
    let mut v = vec![1,2,3,4,5];
    let f = |x| x + 10;

    my_for(&mut v, f);
    assert_eq!(v, vec![11,12,13,14,15]);
}
あまり良くないことではありますが……