Решение на FMI Buzz от Ивайло Атовски

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

Към профила на Ивайло Атовски

Резултати

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

Код

/// Параметри:
/// - полета `k1` и `k2`, които са двата делителя, които ще използваме за заместване.
/// - поле `labels`, които са трите етикета, които съответстват на коефициентите
///
pub struct FizzBuzzer {
pub k1: u8,
pub k2: u8,
pub labels: [String; 3],
}
impl FizzBuzzer {
/// За всяко число от 1 до `n` включително, искаме съответстващия елемент в резултата да е:
///
/// - Първия String от полето `labels` ако числото се дели на k1, но не на k2
/// - Втория String от полето `labels` ако числото се дели на k2, но не на k1
/// - Третия String от полето `labels` ако числото се дели и на k1, и на k2
/// - Числото конвертирано до низ, във всички други случаи
///
/// Ако `n` е 0, очакваме празен вектор за резултат.
/// Ако `k1` или `k2` са 0 или 1, очакваме функцията да panic-не с каквото съобщение изберете.
///
pub fn take(&self, n: usize) -> Vec<String> {
let mut result: Vec<String> = Vec::new();
if self.k1 < 2 || self.k2 < 2 {
panic!("Не може така!!! Тука има правила!")
}
let del1: usize = self.k1 as usize;
let del2: usize = self.k2 as usize;
for num in 1..=n {
let val = match num {
t if t % del1 == 0 && t % del2 != 0 => self.labels[0].clone(),
t if t % del1 != 0 && t % del2 == 0 => self.labels[1].clone(),
t if t % del1 == 0 && t % del2 == 0 => self.labels[2].clone(),
_ => num.to_string(),
};
result.push(val);
}
result
}
/// Параметъра `index` указва кой етикет от полето `labels` променяме, от 0 до 2. Ако подадения
/// `index` е извън тези рамки, очакваме функцията да panic-не.
///
/// Стойността `value` е низа, който ще сложим на този индекс в полето `labels`.
///
pub fn change_label(&mut self, index: usize, value: &String) {
if index > 2 {
panic!("Не може така!!! Тука има правила!")
}
self.labels[index] = value.clone();
}
}
/// Вход: променлива `n`, която описва броя елементи, които ще генерираме в резултата.
///
/// За всяко число от 1 до `n` включително, искаме съответстващия елемент в резултата да е:
///
/// - String със съдържание "Fizz" ако числото се дели на 3, но не на 5
/// - String със съдържание "Buzz" ако числото се дели на 5, но не на 3
/// - String със съдържание "Fizzbuzz" ако числото се дели и на 3, и на 5
/// - Числото конвертирано до низ, във всички други случаи
///
/// Тоест, във `fizzbuzz(3)`, първия елемент ще бъде `String::from("1")`, втория
/// `String::from("2")`, последния `String::from("Fizz")`.
///
/// Ако `n` е 0, очакваме празен вектор за резултат.
///
pub fn fizzbuzz(n: usize) -> Vec<String> {
let fizzbuzzer = FizzBuzzer {
k1: 3,
k2: 5,
labels: [
String::from("Fizz"),
String::from("Buzz"),
String::from("Fizzbuzz"),
],
};
fizzbuzzer.take(n)
}
/// Вход:
/// - променлива `n`, която описва броя елементи, които ще генерираме в резултата.
/// - променливи `k1` и `k2`, които са двата делителя, които ще използваме за заместване.
///
/// За всяко число от 1 до `n` включително, искаме съответстващия елемент в резултата да е:
///
/// - String със съдържание "Fizz" ако числото се дели на k1, но не на k2
/// - String със съдържание "Buzz" ако числото се дели на k2, но не на k1
/// - String със съдържание "Fizzbuzz" ако числото се дели и на k1, и на k2
/// - Числото конвертирано до низ, във всички други случаи
///
/// Ако `n` е 0, очакваме празен вектор за резултат.
/// Ако `k1` или `k2` са 0 или 1, очакваме функцията да panic-не с каквото съобщение изберете.
///
pub fn custom_buzz(n: usize, k1: u8, k2: u8) -> Vec<String> {
let fizzbuzzer = FizzBuzzer {
k1: k1,
k2: k2,
labels: [
String::from("Fizz"),
String::from("Buzz"),
String::from("Fizzbuzz"),
],
};
fizzbuzzer.take(n)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic() {
let expected = vec![1.to_string(), 2.to_string(), String::from("Fizz")];
assert_eq!(fizzbuzz(3), expected);
assert_eq!(custom_buzz(3, 3, 5), expected);
let mut fizzbuzzer = FizzBuzzer {
k1: 3,
k2: 5,
labels: [
String::from("Fizz"),
String::from("Buzz"),
String::from("Fizzbuzz"),
],
};
assert_eq!(fizzbuzzer.take(3), expected);
fizzbuzzer.change_label(0, &String::from("Fiz"));
}
#[test]
#[should_panic]
fn test_more() {
let expected = vec![1.to_string(), 2.to_string(), String::from("Fizz")];
assert_eq!(custom_buzz(3, 3, 1), expected);
}

Ако знаеш, че теста ще panic-не, няма смисъл да слагаш assertion -- изглежда подвеждащо, реално expectation-а не е правилен, защото този код няма да върне това, което assert-ваш.

#[test]
#[should_panic]
fn test_panic_now() {
let mut fizzbuzzer = FizzBuzzer {
k1: 3,
k2: 5,
labels: [
String::from("Fizz"),
String::from("Buzz"),
String::from("Fizzbuzz"),
],
};
fizzbuzzer.change_label(50, &String::from("New"));
}
#[test]
fn test_thiat_same() {
let expected = vec![1.to_string(), 2.to_string(), String::from("Fizz")];
assert_eq!(fizzbuzz(3), expected);
assert_eq!(fizzbuzz(3), custom_buzz(3, 3, 5));
let mut fizzbuzzer = FizzBuzzer {
k1: 3,
k2: 5,
labels: [
String::from("Fizz"),
String::from("Buzz"),
String::from("Fizzbuzz"),
],
};
assert_eq!(fizzbuzzer.take(50), fizzbuzz(50));
fizzbuzzer.change_label(0, &String::from("Fiz"));
assert!(fizzbuzzer.take(50) != fizzbuzz(50))
}
#[test]
fn test_check() {
let mut fizzbuzzer = FizzBuzzer {
k1: 7,
k2: 11,
labels: [
String::from("ЕДНО"),
String::from("ДВЕ"),
String::from("Евала"),
],
};
fizzbuzzer.change_label(2, &String::from("БИНГО"));
let the_vec = fizzbuzzer.take(80);
let mut count_bingo = 0;
for elem in the_vec {
if elem == String::from("БИНГО") {
count_bingo += 1;
}
}
assert_eq!(count_bingo, 1);

Хм, бих казал, че има смисъл просто да провериш 77мия елемент, и да провериш, че други елементи имат други стойности just in case. Тази проверка е малко по-сложна, отколкото е нужно може би, и ако се счупи, би било трудно да намериш защо точно се чупи.

Ако искаш да изразиш "точно 1 елемент има стойност БИНГО", това не е лоша идея за тест, но предвид че знаеш кой е (можеше и да не знаеш), можеш да кажеш "този елемент е БИНГО", последвано от .remove( на този индекс и "всички други елементи НЕ са БИНГО". Макар че още не сме говорили за итератори, така че просто нещо, за което да си мислиш :)

}
}

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

Compiling solution v0.1.0 (/tmp/d20201028-2816268-1aj8xtt/solution)
    Finished test [unoptimized + debuginfo] target(s) in 2.71s
     Running target/debug/deps/solution-ebb42508826ef2b4

running 5 tests
test tests::test_basic ... ok
test tests::test_check ... ok
test tests::test_more ... ok
test tests::test_panic_now ... ok
test tests::test_thiat_same ... ok

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

     Running target/debug/deps/solution_test-9e954a53ed808c89

running 10 tests
test solution_test::test_change_label_basic ... ok
test solution_test::test_change_label_invalid ... ok
test solution_test::test_classic1 ... ok
test solution_test::test_classic2 ... ok
test solution_test::test_coefficients1 ... ok
test solution_test::test_coefficients2 ... ok
test solution_test::test_coefficients_invalid ... ok
test solution_test::test_struct_basic ... ok
test solution_test::test_struct_invalid ... ok
test solution_test::test_zeroes ... ok

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

   Doc-tests solution

running 0 tests

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

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

Ивайло качи първо решение на 26.10.2020 23:27 (преди почти 5 години)

Давам ти бонус точка за тестовете -- имам леки възражения към тях, но по-скоро само към детайлите. Опитал си да упражниш кода с няколко различни комплекти input, така че си на прав път. Бих се погрижил да покрия всички възможни входове за паника, защото сега имаш само по един тест за двете функции, със само един невалиден вход, а това може лесно да се обърка.