LoginSignup
0
0

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-11-22

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

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

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

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

で。

オフラインリアルタイムどう書くE23の実装例 / 再帰を使って書き直した を 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()
    }
}
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++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 または 整数)+ (整数) という危うい計算をしていることに気づいた。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0