オフラインリアルタイムどう書く E23 の実装例( Rust )

問題 : http://nabetani.sakura.ne.jp/hena/orde23nokoch/
実装リンク集 : https://qiita.com/Nabetani/items/77e7e2c749767f197c0e

次回のイベントは 12/8。

問題はもう決まっているけど未完成。やや簡単気味。
rust でも解く予定。

イベントについて詳しくは
https://yhpg.doorkeeper.jp/events/82699
を御覧ください。

で。

[オフラインリアルタイムどう書くE23の実装例 / 再帰を使って書き直した]( https://qiita.com/Nabetani/items/eae9ac532f5e193486f6#%E5%86%8D%E5%B8%B0%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E6%9B%B8%E3%81%8D%E7%9B%B4%E3%81%97%E3%81%9F ) を rust に移植した。

今回はテストフレームワークを使ってみた。

```rust:e23.rs
pub mod kochlike {

    fn v(a: char) -> Vec<i64> {
        match a {
            'a' => vec![0, 5, 1, 0],
            'b' => vec![0, 5, 0, 1, 0],
            _ => panic!("unexpected input"),
        }
    }

    fn m(a: char, ix: i64) -> Option<i64> {
        let vec = v(a);
        if 0 <= ix && (ix as usize) < vec.len() {
            Some(vec[ix as usize])
        } else {
            None
        }
    }

    fn divmod<T: std::ops::Div + std::ops::Rem + Clone>(
        num: T,
        den: T,
    ) -> (<T as std::ops::Div>::Output, <T as std::ops::Rem>::Output) {
        (num.clone() / den.clone(), num % den)
    }

    fn dir_at(seq: String, pos: i64) -> Option<i64> {
        if seq.len() == 0 {
            return Some(0);
        }
        let mul = seq
            .chars()
            .skip(1)
            .fold(1i64, |acc, x| acc * (v(x).len() as i64));
        let (baselen, subpos) = divmod(pos, mul);
        let base = m(seq.chars().next().unwrap(), baselen);
        match base {
            Some(x) => {
                let dir = dir_at(seq.get(1..seq.len()).unwrap().to_string(), subpos);
                match dir {
                    Some(d) => Some(d + x),
                    None => panic!("unexpected"),
                }
            }
            None => None,
        }
    }

    pub fn solve(src: String) -> String {
        let inputs = src
            .split(",")
            .map(|x| x.to_string())
            .collect::<Vec<String>>();
        let num = inputs[0].parse::<i64>().unwrap();
        let rules = inputs[1].clone();
        match dir_at(rules, num - 1i64) {
            Some(x) => vec!["0", "-", "+"][(x % 3) as usize],
            None => "x",
        }.to_string()
    }
}
```

```rust:lib.rs
pub mod e23;

#[cfg(test)]
mod tests {
    use e23::kochlike;

    macro_rules! test {
        ($n:expr, $src:expr, $expected:expr) => {
            eprintln!("{}", $n);
            let actual = kochlike::solve($src.to_string());
            assert_eq!(actual, $expected);
        };
    }

    #[test]
    fn it_works() {
        test!(0, "120,aabb", "0");
        test!(1, "100,a", "x");
        test!(2, "3,a", "-");
        // 中略
        test!(66, "36831104,babbbbbbabab", "+");
    }
}
```

今回大変だったのは、 `divmod` の宣言。
だいぶ苦しんだ。

C++ なら

```c++:c++11
template< typename T >
auto divmod( T const & x, T const & y )-> 
	std::pair<decltype(x/y), decltype(x%y)>
{
	return std::make_pair(x/y, x%y);
}
```

とか書くところ（そもそも divmod を定義する必要がないわけだけど、それはまあいいとして）だけど、rust には `decltype` がない(ないよね？)のでそうは行かない。

なんとかテスト通すところまでは書けたけど、 `clone()` しているのが気に入らない。
今回は `i64` だから `clone()` は安いんだけど、多倍長整数だったりすると困るので `clone()` は避けたいところ。
でも借用では書けなかった。難しい。


テストフレームワークを使い始めるのにも時間がかかった。

* ファイル名が `e23.rs` なら `mod e23;` と書く
* `e23.rs` 内の `mod kochlike{略}` を参照するには `e23::kochlike;` と書く

というところになかなか気づかなかった。

関数 `v` が呼ばれるたびに `Vec` が生成されるのは良くないので 関数の外で `static` な変数として定義するべきだけど、サボった。

あと。
rust に移植したら、ruby 版では （`nil` または 整数）+ （整数） という危うい計算をしていることに気づいた。