Решение на Дигитален корен от Сашо Михайлов

Обратно към всички решения

Към профила на Сашо Михайлов

Резултати

  • 17 точки от тестове
  • 0 бонус точки
  • 17 точки общо
  • 5 успешни тест(а)
  • 1 неуспешни тест(а)

Код

/// Десетична бройна система: 0-9
pub fn decimal(input: &str) -> Option<u32> {
println!("Function decimal() called with input = {} .", input);
if input.is_empty() {
None
}
else {
let mut mut_input = input;
let mut result: u32;
let mut result_as_string: String;
loop {
result = 0;
for c in mut_input.chars() {
result += u32::from_str_radix(&c.to_string()[..], 10).unwrap();

Този unwrap тук ти е докарал един failing тест. Причината u32::from_str_radix да връща Option е защото може да не проработи. При какви обстоятелства няма да работи? Когато входа не е валидно число в тази бройна система. А както казахме, това е точно случай, в който да върнеш None.

Можеше да използваш match statement примерно така:

result += match u32::from_str_radix(&c.to_string()[..], 10) {
    Some(n) => n,
    None => return None,
};

Или можеше да използваш оператора ?, за който говорихме в лекцията за error handling, който работи и за Option, с който горния код изглежда така:

result += u32::from_str_radix(&c.to_string()[..], 10)?;

Поправка -- u32::from_str_radix връща резултат, така че горните снипети биха били:

result += match u32::from_str_radix(&c.to_string()[..], 10) {
    Ok(value) => value,
    Err(_) => return None,
};
result += u32::from_str_radix(&c.to_string()[..], 10).ok()?;
}
result_as_string = result.to_string();
mut_input = &result_as_string[..];
println!("\tmut_input = {} after calculation", mut_input);
if mut_input.len() == 1 {
break;
}
}
Some(result)
}
}
/// Шестнадесетична бройна система: 0-9, последвано от a-f
pub fn hex(input: &str) -> Option<u32> {
println!("Function hex() called with input = {} .", input);
if input.is_empty() {
None
}

Би могъл да спестиш едно ниво на индентация, ако направиш това return None и вместо else просто си продължиш функцията. Това обикновено се нарича "guard clause" -- предпазваш остатъка от кода от невалиден вход като излизаш от функцията рано.

else {
let mut mut_input = input;
let mut result: u32;
let mut result_as_hex;
let mut result_as_string: String;
loop {
result = 0;
for c in mut_input.chars() {
result += u32::from_str_radix(&c.to_string()[..], 16).unwrap();
}
result_as_hex = format!("{:x}", result);
result_as_string = result_as_hex.to_string();

Макроса format! вече ти връща String, така че метода .to_string() тук не прави нищо. Интересното е, че тази променлива не си ѝ сложил тип така или иначе, така че изглежда и ти не си бил сигурен -- щеше да е добре да провериш. В Rust, както и в подобни статично типизирани езици, помага много да знаеш какъв точно тип е всяка една променлива, или поне какви trait bounds има, така че си заслужава да си задаваш тези въпроси.

mut_input = &result_as_string[..];
println!("\tmut_input = {} after calculation", mut_input);
if mut_input.len() == 1 {
break;
}
}

Имаш променливи, които дефинираш извън цикъла и мутираш, което не изглежда да е напълно нужно. result_as_hex примерно се използва само в цикъла, никога извън него, така че вместо да си поддържаш mutable променлива, можеш да имаш една read-only променлива декларирана в цикъла. Дори и result може да е immutable и да съществува само в рамките на цикъла, ако заместиш този break с return Some(result).

Добре е кода да изразява точно каквото прави и нищо повече. Ако една променлива е дефинирана като "mutable" извън цикъла, това комуникира, че тази променлива споделя някаква информация между завъртанията на цикъла и затова е такава. За result_as_string и result_as_hex това не изглежда да е така, но ти трябва известно четене да го осъзнаеш, което е ненужно. Дефинирането им вътре в цикъла комуникира по-ясно, че са временни.

Освен това не е нужно да имаш mut_input. Типа String също има len метод и също може да се печата. Няма причина да променяш и тази променлива, доколкото виждам. Basically, мисли за това кои части от кода наистина има нужда да са такива, и кои могат да се орежат като излишни -- кратко и ясно решение на проблем се чете по-лесно :).

Some(result)
}
}
/// Осмична бройна система: 0-7
pub fn octal(input: &str) -> Option<u32> {
println!("Function octal() called with input = {} .", input);
if input.is_empty() {
None
}
else {
let mut mut_input = input;
let mut result: u32;
let mut result_as_octal;
let mut result_as_string: String;
loop {
result = 0;
for c in mut_input.chars() {
result += u32::from_str_radix(&c.to_string()[..], 8).unwrap();
}
result_as_octal = format!("{:o}", result);
result_as_string = result_as_octal.to_string();
mut_input = &result_as_string[..];
println!("\tmut_input = {} after calculation", mut_input);
if mut_input.len() == 1 {
break;
}
}
Some(result)
}
}
/// Двоична бройна система: 0-1
pub fn binary(input: &str) -> Option<u32> {
println!("Function binary() called with input = {} .", input);
if input.is_empty() {
None
}
else {
if input.contains("1") {
Some(1)
}
else {
Some(0)
}
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20200111-2173579-qh2cvp/solution)
    Finished test [unoptimized + debuginfo] target(s) in 1.88s
     Running target/debug/deps/solution-a73e64ec87929bd0

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/solution_test-38971695424b36d5

running 6 tests
test solution_test::test_binary ... ok
test solution_test::test_decimal_basic ... ok
test solution_test::test_hex_basic ... ok
test solution_test::test_invalid ... FAILED
test solution_test::test_octal_basic ... ok
test solution_test::test_zeroes ... ok

failures:

---- solution_test::test_invalid stdout ----
Function decimal() called with input = f .
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', src/libcore/result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.


failures:
    solution_test::test_invalid

test result: FAILED. 5 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

История (1 версия и 6 коментара)

Сашо качи първо решение на 02.11.2019 23:02 (преди почти 6 години)

Имаш работещо решение, но е малко sloppy. Примерно, използваш unwrap, което ти е изгубило точки, а не виждам много добра причина да го правиш. Имаш и доста променливи и конвертиране, което е ненужно. Изглежда ми като че може би не си вложил време да си опростиш решението или да обмислиш потенциални edge cases -- съветвам те да си дадеш повече време за следващото :).