Matrix 4

Предадени решения

Краен срок:
18.11.2021 17:00
Точки:
20

Срокът за предаване на решения е отминал

use solution::*;
macro_rules! i32_cell_vec {
($($x : expr), + $(,) ?) => {
vec![ $($x),* ].into_iter().map(Cell).collect::<Vec<Cell<i32>>>()
}
}
macro_rules! string_cell_vec {
($($x : expr), + $(,) ?) => {
vec![ $($x),* ].into_iter().map(String::from).map(Cell).collect::<Vec<Cell<String>>>()
}
}
#[test]
fn test_iterating_i32s() {
assert_eq!(Matrix::new(&[1, 2, 3, 4]).by_row(), i32_cell_vec![1, 2, 3, 4]);
assert_eq!(Matrix::new(&[13, 42, 37, 24]).by_row(), i32_cell_vec![13, 42, 37, 24]);
assert_eq!(Matrix::new(&[1, 2, 3, 4]).by_col(), i32_cell_vec![1, 3, 2, 4]);
assert_eq!(Matrix::new(&[13, 42, 37, 24]).by_col(), i32_cell_vec![13, 37, 42, 24]);
}
#[test]
fn test_iterating_strings() {
let matrix = Matrix::new(&[String::from("a"), String::from("b"), String::from("c"), String::from("d")]);
assert_eq!(matrix.by_row(), string_cell_vec!["a", "b", "c", "d"]);
let matrix = Matrix::new(&[String::from("a"), String::from("b"), String::from("c"), String::from("d")]);
assert_eq!(matrix.by_col(), string_cell_vec!["a", "c", "b", "d"]);
}
#[test]
fn test_adding_int_and_string_positive() {
let cell1 = Cell(4);
let cell2 = Cell(String::from("badger"));
assert_eq!((cell1 + cell2).0, String::from("4 badger"));
}
#[test]
fn test_adding_int_and_string_zero() {
let cell1 = Cell(0);
let cell2 = Cell(String::from("badger"));
assert_eq!((cell1 + cell2).0, String::from("0 badger"));
}
#[test]
fn test_adding_int_and_string_negative() {
let cell1 = Cell(-2);
let cell2 = Cell(String::from("badger"));
assert_eq!((cell1 + cell2).0, String::from("regdab 2"));
}
#[test]
fn test_adding_int_and_string_unicode() {
let string_cell = Cell(String::from("опа"));
assert_eq!((Cell(0) + string_cell.clone()).0, String::from("0 опа"));
assert_eq!((Cell(2) + string_cell.clone()).0, String::from("2 опа"));
assert_eq!((Cell(-3) + string_cell.clone()).0, String::from("апо 3"));
}
#[test]
fn test_multiplying_int_and_string_positive() {
let cell1 = Cell(4);
let cell2 = Cell(String::from("badger"));
assert_eq!((cell1 * cell2).0, String::from("badgerbadgerbadgerbadger"));
}
#[test]
fn test_multiplying_int_and_string_zero() {
let cell1 = Cell(0);
let cell2 = Cell(String::from("badger"));
assert_eq!((cell1 * cell2).0, String::from(""));
}
#[test]
fn test_multiplying_int_and_string_negative() {
let cell1 = Cell(-2);
let cell2 = Cell(String::from("woo!"));
assert_eq!((cell1 * cell2).0, String::from("!oow!oow"));
}
#[test]
fn test_multiplying_int_and_string_unicode() {
let string_cell = Cell(String::from("опа"));
assert_eq!((Cell(0) * string_cell.clone()).0, String::from(""));
assert_eq!((Cell(2) * string_cell.clone()).0, String::from("опаопа"));
assert_eq!((Cell(-3) * string_cell.clone()).0, String::from("апоапоапо"));
}
#[test]
fn test_blank_strings() {
assert_eq!((Cell(375) * Cell(String::from(""))).0, String::from(""));
assert_eq!((Cell(573) + Cell(String::from(""))).0, String::from("573 "));
}
#[test]
fn test_adding_matrices_1() {
let matrix1 = Matrix::new(&[1, 2, 3, 4]);
let matrix2 = Matrix::new(&[
String::from("one"), String::from("two"),
String::from("three"), String::from("four")
]);
assert_eq!((matrix1 + matrix2).by_row(), string_cell_vec!["1 one", "2 two", "3 three", "4 four"]);
}
#[test]
fn test_adding_matrices_2() {
let matrix1 = Matrix::new(&[1, 0, -3, -37]);
let matrix2 = Matrix::new(&[
String::from("едно"), String::from("две"),
String::from(" "), String::from("четири ")
]);
assert_eq!((matrix1 + matrix2).by_row(), string_cell_vec!["1 едно", "0 две", " 3", " иритеч 37"]);
}
#[test]
fn test_multiplying_matrices_1() {
let matrix1 = Matrix::new(&[
1, 2,
3, 1
]);
let matrix2 = Matrix::new(&[
String::from("one"), String::from("two"),
String::from("three"), String::from("you get it")
]);
assert_eq!(matrix1 * matrix2, String::from("one threethree twotwotwo you get it"));
}
#[test]
fn test_multiplying_matrices_2() {
let matrix1 = Matrix::new(&[
1, 0,
-3, -2,
]);
let matrix2 = Matrix::new(&[
String::from("едно"), String::from("две"),
String::from(" "), String::from("четири ")
]);
assert_eq!(matrix1 * matrix2, String::from("едно евдевдевд иритеч иритеч"));
}

Изненадващо, култовия филм "Матрицата" получава 4та версия скоро... Woah. Оригинала, който излезе (checks notes) преди някои от вас да са родени, е cyberpunk класика, и в негова чест ще си поиграем и ние с матрици с четири измерения елемента.

Започваме с конструиране и итериране на матрици (с четири елемента разделени на два реда) и на клетки от елементи:

#[derive(Debug)]
pub struct Matrix<T: Clone> {
    // Каквито данни ви вършат работа
}

#[derive(Debug, Clone, PartialEq)]
pub struct Cell<T>(pub T);

impl<T: Clone> Matrix<T> {
    /// Данните се очаква да бъдат подадени със статичен масив -- вижте по-долу за примери за
    /// конструиране. Какви може да са елементите? Ще тестваме само с два типа: String и i32.
    ///
    /// Очаква се да бъдат подадени по редове, от ляво надясно. Тоест, ако подадем като вход списък
    /// с елементи: 1, 2, 3, 4, се очаква конструираната матрица:
    ///
    /// | 1 2 |
    /// | 3 4 |
    ///
    /// Забележете, че подаваме като вход някакъв slice -- reference тип. Не очакваме матрицата да
    /// държи reference, клонирайте си данните, за да имате ownership.
    ///
    pub fn new(data: &[T; 4]) -> Matrix<T> {
        todo!()
    }

    /// Връща вектор, който съдържа в себе си всички 4 елемента на матрицата, наредени по редове,
    /// от ляво надясно и от горе надолу, обвити в `Cell`. Тоест, ако матрицата изглежда така:
    ///
    /// | 1 2 |
    /// | 3 4 |
    ///
    /// Очакваме `.by_row` да върне елементите в ред: 1, 2, 3, 4
    ///
    pub fn by_row(&self) -> Vec<Cell<T>> {
        todo!()
    }

    /// Връща вектор, който съдържа в себе си всички 4 елемента на матрицата, наредени по колони,
    /// от горе надолу и от ляво надясно, Обвити в `Cell`. Тоест, ако матрицата изглежда така:
    ///
    /// | 1 2 |
    /// | 3 4 |
    ///
    /// Очакваме `.by_col` да върне елементите в ред: 1, 3, 2, 4
    ///
    pub fn by_col(&self) -> Vec<Cell<T>> {
        todo!()
    }
}

Какви данни да съхранявате в матрицата? Можете да съхранявате елементите както са дадени и да конструирате Cell-ове при итерация, или можете да си съхранявате Cell-ове и да ги клонирате. Може да бъде вектор, а може и нещо по-просто. Може да ги държите по редове или по колони, или даже и двете. Ваш избор. Важното е да си имате ownership над тях, за да не се стига до lifetime анотации (тях ще ги упражним в бъдещи домашни и предизвикателства).

Забележете, че на двата struct-а сме сложили derive-нати типове: Debug, Clone, PartialEq. Оставете ги както сме ги дали, макар че технически погледнато не ви пречи да ги имплементирате сами (няма нужда).

(Идея: дали не можете да си напишете собствена имплементация на PartialEq, която просто казва, че всички клетки са равни една на друга и да минете хитро през тестовете 😈? Можете! И ако го направите, ще въздъхнем тежко, и ще копи-пейстнем примерно assert_ne!(Cell(0), Cell(1)) във всеки един от афектираните тестове, и ще ви смъкнем малко точки задето ни създавате допълнителна работа. Be smart, don't be clever.)

Събиране и умножение на клетки

Сега започваме с по-интересните неща. Матриците и клетките са generic, но за целите на задачата, ще работим само със String и i32. Нещо повече, събирането на числа и събирането на низове поотделно са сравнително скучни, така че ще имплементирате събиране само и единствено на i32 и String в този ред -- число отляво, низ отдясно (не е трудно да имплементирате огледалната операция, не си заслужава да ви губим времето).

Как се събира число-клетка с низ-клетка? Ако числото е положително или нула, просто конкатенираме двете неща като низ с интервал между тях:

assert_eq!(Cell(22) + Cell(String::from("years ago")), Cell(String::from("22 years ago")))
assert_eq!(Cell(0) + Cell(String::from("expectation")), Cell(String::from("0 expectation")))

Ако числото е отрицателно, обръщаме текста на низа и го слагаме преди числото по абсолютна стойност:

assert_eq!(Cell(-4) + Cell(String::from("xirtam")), Cell(String::from("matrix 4")))

Очакваме да имплементирате само събиране на Cell<i32> отляво със Cell<String> отдясно, като резултата трябва да е Cell<String>. Би трябвало това да ви е достатъчно информация, за имплементирате събирането с trait-а std::ops::Add. Стига да се справите, горните примери (и базовия тест) ще ви се компилират.

Как се умножава число-клетка с низ-клетка? Ако числото е положително или нула, повтаряме толкова пъти низа:

assert_eq!(Cell(3) * Cell(String::from("woah!")), Cell(String::from("woah!woah!woah!")))
assert_eq!(Cell(0) * Cell(String::from("woah?")), Cell(String::from("")))

Ако числото е отрицателно, обръщаме текста на низа и го повтаряме толкова пъти

assert_eq!(Cell(-3) * Cell(String::from(",regdab")), Cell(String::from("badger,badger,badger,")))

Също както горния пример, умножението има Cell<i32> от лявата страна и Cell<String> от дясната, и резултата е Cell<String>. Trait-а е std::ops::Mul

Събиране и умножение на матрици

Знаейки как се правят операции с клетки, може да дефинираме как стават нещата с матрици. Съществен разлика е, че събирането ни дава нова матрица, а умножението -- един-единствен низ. Събираме матрици поелементно -- всеки елемент на определен ред и колона в лявата матрица с елемента на същите координати в дясната:

| 1 2 | + | "one"   "two"  | = | "1 one"   "2 two"  |
| 3 4 |   | "three" "four" |   | "3 three" "4 four" |

Умножението на матрици става по правилото "редове по колони" -- елементите от първия ред на лявата матрица са умножени поелементно с първата колона на дясната, втория ред със втората колона, и са събрани в един низ с интервали:

| 1 2 | * | "one"   "two"        | = "one threethree twotwotwo you get it"
| 3 1 |   | "three" "you get it" |

Тук "събиране" и "умножение" работят по правилата на клетките по-горе. Вижте базовия тест за примери, но типовете са:

  • Matrix<i32> + Matrix<String> = Matrix<String>
  • Matrix<i32> * Matrix<String> = String

Trait-овете както по-горе са std::ops::Add и std::ops::Mul.

Важно!

Погрижете се да имате версия на Rust компилатора и на cargo поне 1.56.0. Може да проверите като извикате rustc --version и cargo --version. Проверете и че в Cargo.toml файла си имате следния ред:

edition = "2021"

В случай, че версиите ви са стари, може да използвате тази команда, за да си ги обновите:

rustup update stable

Задължително прочетете (или си припомнете): Указания за предаване на домашни

Погрижете се решението ви да се компилира с базовия тест:

use solution::*;
// За помощни цели:
fn string_cell_vec(s1: &str, s2: &str, s3: &str, s4: &str) -> Vec<Cell<String>> {
[s1, s2, s3, s4].into_iter().map(String::from).map(Cell).collect::<Vec<Cell<String>>>()
}
#[test]
fn test_basic() {
assert_eq!((Cell(4) + Cell(String::from("badger"))).0, String::from("4 badger"));
assert_eq!((Cell(2) * Cell(String::from("mushroom"))).0, String::from("mushroommushroom"));
let matrix1 = Matrix::new(&[1, 2, 3, 4]);
let matrix2 = Matrix::new(&[
String::from("one"), String::from("two"),
String::from("three"), String::from("four")
]);
assert_eq!(matrix1.by_row()[0], Cell(1));
assert_eq!(matrix1.by_col()[0], Cell(1));
assert_eq!(
(matrix1 + matrix2).by_row(),
string_cell_vec("1 one", "2 two", "3 three", "4 four")
);
let matrix1 = Matrix::new(&[1, 1, 1, 1]);
let matrix2 = Matrix::new(&[
String::from("one"), String::from("two"),
String::from("three"), String::from("four")
]);
assert_eq!(matrix1 * matrix2, String::from("one three two four"));
}