問題 : http://nabetani.sakura.ne.jp/hena/orde23nokoch/
実装リンク集 : https://qiita.com/Nabetani/items/77e7e2c749767f197c0e
次回のイベントは 12/8。
問題はもう決まっているけど未完成。やや簡単気味。
rust でも解く予定。
イベントについて詳しくは
https://yhpg.doorkeeper.jp/events/82699
を御覧ください。
で。
オフラインリアルタイムどう書くE23の実装例 / 再帰を使って書き直した を rust に移植した。
今回はテストフレームワークを使ってみた。
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()
}
}
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++ なら
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
または 整数)+ (整数) という危うい計算をしていることに気づいた。