Rust の練習としてLINEのコーディングをやってみました。以前からずっとやりたかったのですが、問題も長くて面倒でやってなかったんですよね(実際めんどうだった)…
今回解いた問題
以下解説していきます
(注: 入出力の例が一切なく、ジャッジシステムも見当たらなかったので、コードが問題の意図通り動いているか保証されていません。仕様の誤読やコーディングミスがある恐れがあります)
問題概要
問題本文はこちら
タクシーの走行記録が入力として渡されるので料金を求めよ。
- 走行距離に応じた料金と、低速走行時間に応じた料金の合計が料金
- 走行距離は初乗り410円、1052m以降は237mごとに80円
- ただし、深夜時間帯は走った距離が1.25倍されて計算される
- 低速走行とは、10km/h以下で走ることを指す。降車までの総低速時間に対して90秒ごとに80円かかる。
ざっと要約するとこんな感じです。
方針
仕様があまりに複雑なので、テスト駆動っぽく解きました。
- 走行距離のみ考慮して計算する(深夜時間帯、低速走行を含まないケースのテストを書く)
- 深夜時間帯を考慮して計算する
- 低速走行を考慮して計算する
だいたいこんな感じで段階を踏みながら関数をリファクタリングしていきました。
実際のコード
テストコードを抜いておよそ80行になりました。
use std::io::{self, BufRead, BufReader};
type Record = Vec<(f64, f64)>;
fn to_sec(h: u32, m: u32, s: f64) -> f64 {
((h * 60 + m) * 60) as f64 + s
}
fn is_midnight(s: f64) -> bool {
match s {
t if t < to_sec(5, 0, 0.0) => true,
t if t >= to_sec(22, 0, 0.0) => true,
_ => false,
}
}
fn measure_distance(v: &Record) -> f64 {
let mut distance = v.into_iter().fold(0.0, |sum, (_, meter)| sum + meter);
for x in v.windows(2) {
let (t1, ..) = x[0];
let (t2, meter2) = x[1];
if is_midnight(t1) && is_midnight(t2) {
distance = distance + 0.25 * meter2;
}
}
distance
}
fn sum_slow_running_time(v: &Record) -> f64 {
let mut slow_running_time = 0.0;
for x in v.windows(2) {
let (t1, ..) = x[0];
let (t2, meter2) = x[1];
let dt = t2 - t1;
if (meter2 / dt) * 60.0 * 60.0 > 10000.0 {
continue;
}
slow_running_time += if is_midnight(t1) && is_midnight(t2) {
dt * 1.25
} else {
dt
}
}
slow_running_time
}
fn calc_distance_based_fare(v: &Record) -> u32 {
410 + ((measure_distance(v) - 1052.0) / 237.0).ceil() as u32 * 80
}
fn calc_slow_fare(v: &Record) -> u32 {
(sum_slow_running_time(v) / 90.0).floor() as u32 * 80
}
fn calc_fare(v: &Record) -> u32 {
calc_distance_based_fare(v) + calc_slow_fare(v)
}
fn read_from_stdin() -> Record {
let mut time_records: Record = vec![];
let mut reader = BufReader::new(io::stdin());
let mut s = String::new();
while reader.read_line(&mut s).expect("Failed to read line.") > 0 {
let v: Vec<_> = s.split_whitespace().collect();
let meter = v[1].parse().unwrap_or(0.0);
let time: Vec<_> = v[0].split(':').collect();
let hour = time[0].parse().unwrap_or(0);
let min = time[1].parse().unwrap_or(0);
let sec = time[2].parse().unwrap_or(0.0);
time_records.push((to_sec(hour, min, sec), meter));
s.clear();
}
time_records
}
fn main() {
let time_records = read_from_stdin();
println!("{}", calc_fare(&time_records));
}
テストコードはこんな感じです。
#[test]
fn distance_based_fare() {
let record = vec![(to_sec(7, 0, 0.0), 0.0), (to_sec(7, 1, 0.0), 1052.0)];
assert_eq!(measure_distance(&record), 1052.0);
assert_eq!(calc_distance_based_fare(&record), 410);
let record = vec![(to_sec(7, 0, 0.0), 0.0), (to_sec(7, 1, 0.0), 1053.0)];
assert_eq!(measure_distance(&record), 1053.0);
assert_eq!(calc_distance_based_fare(&record), 490);
let record = vec![(to_sec(7, 0, 0.0), 0.0), (to_sec(7, 1, 0.0), 1289.0)];
assert_eq!(measure_distance(&record), 1289.0);
assert_eq!(calc_distance_based_fare(&record), 490);
let record = vec![(to_sec(7, 0, 0.0), 0.0), (to_sec(7, 1, 0.0), 1290.0)];
assert_eq!(measure_distance(&record), 1290.0);
assert_eq!(calc_distance_based_fare(&record), 570);
}
#[test]
fn midnight_fare() {
let record = vec![(to_sec(0, 0, 0.0), 0.0), (to_sec(0, 1, 0.0), 841.0)];
assert_eq!(measure_distance(&record), 1051.25);
assert_eq!(calc_distance_based_fare(&record), 410);
let record = vec![(to_sec(0, 0, 0.0), 0.0), (to_sec(0, 1, 0.0), 842.0)];
assert_eq!(measure_distance(&record), 1052.5);
assert_eq!(calc_distance_based_fare(&record), 490);
let record = vec![(to_sec(0, 0, 0.0), 0.0), (to_sec(0, 1, 0.0), 1031.0)];
assert_eq!(measure_distance(&record), 1288.75);
assert_eq!(calc_distance_based_fare(&record), 490);
let record = vec![(to_sec(0, 0, 0.0), 0.0), (to_sec(0, 1, 0.0), 1031.3)];
assert_eq!(measure_distance(&record), 1289.125);
assert_eq!(calc_distance_based_fare(&record), 570);
}
#[test]
fn midnight_fare2() {
let record = vec![
(to_sec(21, 59, 0.0), 0.0),
(to_sec(22, 0, 0.0), 927.0),
(to_sec(22, 0, 10.0), 100.0),
];
assert_eq!(measure_distance(&record), 1052.0);
assert_eq!(calc_distance_based_fare(&record), 410);
let record = vec![
(to_sec(21, 59, 0.0), 0.0),
(to_sec(22, 0, 0.0), 927.1),
(to_sec(22, 0, 10.0), 100.0),
];
assert_eq!(measure_distance(&record), 1052.1);
assert_eq!(calc_distance_based_fare(&record), 490);
let record = vec![
(to_sec(21, 59, 0.0), 0.0),
(to_sec(22, 0, 0.0), 927.0),
(to_sec(22, 0, 10.0), 100.1),
];
assert_eq!(measure_distance(&record), 1052.125);
assert_eq!(calc_distance_based_fare(&record), 490);
}
#[test]
fn slow_fare() {
let record = vec![(to_sec(7, 0, 0.0), 0.0), (to_sec(8, 0, 0.0), 10000.1)];
assert_eq!(sum_slow_running_time(&record), 0.0);
let record = vec![(to_sec(7, 0, 0.0), 0.0), (to_sec(8, 0, 0.0), 10000.0)];
assert_eq!(sum_slow_running_time(&record), 3600.0);
let record = vec![(to_sec(0, 0, 0.0), 0.0), (to_sec(1, 0, 0.0), 10000.0)];
assert_eq!(sum_slow_running_time(&record), 3600.0 * 1.25);
}