упр.04 задача 2
- Краен срок:
- 05.11.2025 23:59
- Точки:
- 2
Срокът за предаване на решения е отминал
// Include the solution source in the same file, so we
// don't have to worry about item visibility.
// Please don't use `include!` in real code, this is a hack
// around the checking system.
include!{ "../src/lib.rs" }
fn get_books() -> Vec<Book> {
vec![
Book {
title: "The Rust Book".into(),
author: "Steve".into(),
year: 2018,
rating: 4.7,
},
Book {
title: "Rust Patterns".into(),
author: "Anna".into(),
year: 2020,
rating: 4.9,
},
Book {
title: "C Programming".into(),
author: "Dennis".into(),
year: 1978,
rating: 4.3,
},
]
}
fn approx_eq(a: f32, b: f32) -> bool {
let eps = 100.0 * f32::EPSILON;
if a.is_nan() || b.is_nan() || a.signum() != b.signum() {
return false;
}
((a - b).abs() / f32::max(a, b)) < eps
}
macro_rules! assert_approx_eq {
($a: expr, $b: expr) => {
if !approx_eq($a, $b) {
panic!("assertion approx_eq failed: left={:?}, right={:?}", $a, $b);
}
};
}
#[test]
fn test_compare() {
let books = get_books();
assert_eq!(books[0].compare(&books[0]), CompareResult::Equal);
assert_eq!(books[0].compare(&books[1]), CompareResult::Less);
assert_eq!(books[1].compare(&books[0]), CompareResult::Greater);
}
#[test]
fn test_matches() {
let books = get_books();
assert_eq!(books[0].matches("Rust"), true);
assert_eq!(books[2].matches("Rust"), false);
}
#[test]
fn test_aggregate() {
let books = get_books();
assert!(Book::aggregate(&[]).is_nan() || Book::aggregate(&[]) == 0.0);
assert_eq!(Book::aggregate(&books[0..1]), 4.7);
assert_approx_eq!(Book::aggregate(&books[1..]), 4.6);
assert_approx_eq!(Book::aggregate(&books[..]), 4.633333);
}
#[test]
fn test_process() {
let (result, avg_rating) = process_items(get_books(), "Rust", SortOrder::Desc);
assert_eq!(
result.iter().map(|book| book.title.as_str()).collect::<Vec<_>>(),
vec!["Rust Patterns", "The Rust Book"]
);
assert_approx_eq!(avg_rating, 4.8);
let (result, avg_rating) = process_items(get_books(), "Rust", SortOrder::Desc);
assert_eq!(
result.iter().map(|book| book.title.as_str()).collect::<Vec<_>>(),
vec!["Rust Patterns", "The Rust Book"]
);
assert_approx_eq!(avg_rating, 4.8);
}
Цел: Да се реализира библиотека, която работи с произволни типове данни, като им предоставя три функционалности:
- Филтриране по текстова заявка.
- Сортиране, базирано на собствена система от сравнения (без
Ordering). - Агрегиране, което изчислява "стойност" за целия списък според типа на елементите.
Описание
1. Създай собствен тип за сравнение:
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CompareResult {
Less,
Equal,
Greater,
}
2. Създай trait Compare:
trait Compare {
fn compare(&self, other: &Self) -> CompareResult;
}
3. Създай trait Filter:
trait Filter {
fn matches(&self, query: &str) -> bool;
}
4. Създай trait Aggregate:
trait Aggregate {
type Output;
fn aggregate(items: &[Self]) -> Self::Output
where
Self: Sized;
}
// Забележка: не сме говорили за асоциирани типове.
// Идеята в случая е, че Аggregate за книги ще върне f32, докато Aggregate за друг тип може да върне нещо друго.
// Очаква се да напишете
//
// impl Aggregate for Book {
// type Output = f32;
// ...
5. Имплементирай функция:
fn process_items<T>(items: Vec<T>, query: &str, order: SortOrder) -> (Vec<T>, T::Output)
where
T: Compare + Filter + Aggregate + Clone,
{
// TODO: имплементация
}
като имате
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SortOrder {
Desc,
Asc,
}
Примерен тип
#[derive(Clone)]
struct Book {
title: String,
author: String,
year: u32,
rating: f32,
}
Имплементации:
-
matchesвръщаtrue, акоqueryсе съдържа вtitleилиauthor. -
compareсравнява поyear, като по-новите са “по-големи”. -
aggregateизчислява среденratingза всички книги.
Допълнителни усложнения
-
Aggregateтрябва да има асоцииран тип (type Output). -
process_itemsтрябва да използва правилно асоциирания тип. - Не може да се използват
std::cmp::Ordering,sort_by,Ord,PartialOrd. - Да се внимава с borrow checker-а при сортиране.
- Функцията връща два различни типа (вектор + агрегат).
- Примерен резултат
fn main() {
let books = vec![
Book { title: "The Rust Book".into(), author: "Steve".into(), year: 2018, rating: 4.7 },
Book { title: "Rust Patterns".into(), author: "Anna".into(), year: 2020, rating: 4.9 },
Book { title: "C Programming".into(), author: "Dennis".into(), year: 1978, rating: 4.3 },
];
let (result, avg_rating) = process_items(books, "Rust", SortOrder::Asc);
println!("Filtered & sorted:");
for b in &result {
println!("{} by {} ({})", b.title, b.author, b.year);
}
println!("Average rating: {:.2}", avg_rating);
}
Очакван изход:
Filtered & sorted:
The Rust Book by Steve (2018)
Rust Patterns by Anna (2020)
Average rating: 4.80
Задължително прочетете (или си припомнете): Указания за предаване на домашни
Погрижете се решението ви да се компилира с базовия тест:
// Include the solution source in the same file, so we
// don't have to worry about item visibility.
// Please don't use `include!` in real code, this is a hack
// around the checking system.
include!{ "../src/lib.rs" }
fn get_books() -> Vec<Book> {
vec![
Book {
title: "The Rust Book".into(),
author: "Steve".into(),
year: 2018,
rating: 4.7,
},
Book {
title: "Rust Patterns".into(),
author: "Anna".into(),
year: 2020,
rating: 4.9,
},
Book {
title: "C Programming".into(),
author: "Dennis".into(),
year: 1978,
rating: 4.3,
},
]
}
fn approx_eq(a: f32, b: f32) -> bool {
let eps = 100.0 * f32::EPSILON;
if a.is_nan() || b.is_nan() || a.signum() != b.signum() {
return false;
}
((a - b).abs() / f32::max(a, b)) < eps
}
macro_rules! assert_approx_eq {
($a: expr, $b: expr) => {
if !approx_eq($a, $b) {
panic!("assertion approx_eq failed: left={:?}, right={:?}", $a, $b);
}
};
}
#[test]
fn test_compare() {
let books = get_books();
assert_eq!(books[0].compare(&books[0]), CompareResult::Equal);
assert_eq!(books[0].compare(&books[1]), CompareResult::Less);
assert_eq!(books[1].compare(&books[0]), CompareResult::Greater);
}
#[test]
fn test_matches() {
let books = get_books();
assert_eq!(books[0].matches("Rust"), true);
assert_eq!(books[2].matches("Rust"), false);
}
#[test]
fn test_aggregate() {
let books = get_books();
assert!(Book::aggregate(&[]).is_nan() || Book::aggregate(&[]) == 0.0);
assert_eq!(Book::aggregate(&books[0..1]), 4.7);
assert_approx_eq!(Book::aggregate(&books[1..]), 4.6);
assert_approx_eq!(Book::aggregate(&books[..]), 4.633333);
}
#[test]
fn test_process() {
let (result, avg_rating) = process_items(get_books(), "Rust", SortOrder::Desc);
assert_eq!(
result.iter().map(|book| book.title.as_str()).collect::<Vec<_>>(),
vec!["Rust Patterns", "The Rust Book"]
);
assert_approx_eq!(avg_rating, 4.8);
let (result, avg_rating) = process_items(get_books(), "Rust", SortOrder::Desc);
assert_eq!(
result.iter().map(|book| book.title.as_str()).collect::<Vec<_>>(),
vec!["Rust Patterns", "The Rust Book"]
);
assert_approx_eq!(avg_rating, 4.8);
}
