hardware
This commit is contained in:
parent
f2b07c9f30
commit
17f37947f3
8 changed files with 485 additions and 16 deletions
85
20-dev/00-rust/rustlings/Untitled.md
Normal file
85
20-dev/00-rust/rustlings/Untitled.md
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
В функциональном стиле, когда тебе нужно свести последовательность значений к одному числу (произведению в данном случае), используют **свертку** (fold) или, в более простых случаях, готовые методы агрегации вроде `product`.
|
||||
|
||||
В Rust у итераторов есть метод `product()`, который делает ровно то, что нужно для факториала.
|
||||
|
||||
### Решение 1: Самое идиоматичное (Iterators + product)
|
||||
|
||||
Мы создаем диапазон от `1` до `num` (включительно) и перемножаем все числа.
|
||||
|
||||
```rust
|
||||
fn factorial(num: u64) -> u64 {
|
||||
(1..=num).product()
|
||||
}
|
||||
```
|
||||
|
||||
**Почему это работает:**
|
||||
1. `(1..=num)` создает итератор, генерирующий числа `1, 2, 3, ..., num`.
|
||||
* Если `num` равно `0`, диапазон `1..=0` пуст.
|
||||
2. `.product()` проходит по итератору, умножая аккумулятор на каждый элемент.
|
||||
* Для пустого итератора (факториал 0) `product()` возвращает мультипликативную единицу (`1`), что математически верно: $0! = 1$.
|
||||
|
||||
***
|
||||
|
||||
### Решение 2: Более явное (Fold)
|
||||
|
||||
Если бы метода `product()` не существовало, мы бы использовали `fold`. Это классическая "reduce" операция из функционального программирования.
|
||||
|
||||
```rust
|
||||
fn factorial(num: u64) -> u64 {
|
||||
(1..=num).fold(1, |acc, x| acc * x)
|
||||
}
|
||||
```
|
||||
|
||||
**Разбор:**
|
||||
* `1` (первый аргумент) — начальное значение аккумулятора.
|
||||
* `|acc, x| acc * x` — замыкание, которое берет текущий аккумулятор (`acc`) и следующий элемент (`x`), и возвращает новое значение аккумулятора.
|
||||
* Для `num = 4`:
|
||||
1. `acc = 1` (начальное), `x = 1` -> `1 * 1 = 1`
|
||||
2. `acc = 1`, `x = 2` -> `1 * 2 = 2`
|
||||
3. `acc = 2`, `x = 3` -> `2 * 3 = 6`
|
||||
4. `acc = 6`, `x = 4` -> `6 * 4 = 24`
|
||||
|
||||
**Итог:**
|
||||
Используй `(1..=num).product()` — это самый "ржавый" (Rustacean) и читаемый способ. Он выполняет все твои условия:
|
||||
* Нет явного `return` (это выражение).
|
||||
* Нет циклов `for/while` (спрятаны внутри итератора).
|
||||
* Нет мутабельных переменных.
|
||||
* Нет рекурсии.
|
||||
|
||||
|
||||
### 1. "Джентльменский набор" итераторов
|
||||
Помимо уже знакомых тебе `map`, `filter`, `fold` и `flat_map`, вот что ты будешь использовать в 90% случаев:
|
||||
|
||||
* **`collect()`** — ты уже знаешь, это "материализатор". Превращает поток обратно в коллекцию (`Vec`, `HashMap`, `String`...).
|
||||
* **`find(|x| ...)`** — ищет **первый** элемент, удовлетворяющий условию. Возвращает `Option<T>`.
|
||||
* **`any(|x| ...)` / `all(|x| ...)`** — возвращают `bool`. `any` проверяет, есть ли *хотя бы один*, `all` — что *все* соответствуют.
|
||||
* **`enumerate()`** — добавляет индекс к элементу. Вместо `x` получаешь `(index, x)`.
|
||||
```rust
|
||||
// Вывести строки с номерами
|
||||
lines.iter().enumerate().for_each(|(i, line)| println!("{}: {}", i, line));
|
||||
```
|
||||
* **`zip(other_iter)`** — "сшивает" два итератора в один поток пар `(a, b)`.
|
||||
* **`take(n)` / `skip(n)`** — берет первые `n` или пропускает первые `n` элементов.
|
||||
* **`chain(other_iter)`** — склеивает два итератора: "сначала всё из первого, потом всё из второго".
|
||||
* **`inspect(|x| ...)`** — бесценно для отладки! Позволяет "подсмотреть" значение в середине цепочки, не меняя его (обычно там делают `println!`).
|
||||
|
||||
### 2. Вся ли функциональщина в Rust — это итераторы?
|
||||
И да, и нет.
|
||||
* **Да**, итераторы — это главный локомотив функционального стиля в Rust. Это самый мощный и оптимизированный инструмент для обработки данных.
|
||||
* **Нет**, ФП в Rust шире. Оно включает:
|
||||
* **Enum + Pattern Matching** (алгебраические типы данных) — это основа логики, замена классам и полиморфизму.
|
||||
* **Immutability** (неизменяемость по умолчанию).
|
||||
* **Closures** (замыкания) — функции высшего порядка, которые можно передавать и возвращать.
|
||||
|
||||
Но именно цепочки итераторов делают код "визуально функциональным".
|
||||
|
||||
### 3. Каждую ли задачу можно решить функционально?
|
||||
**Теоретически — да.** Любой цикл можно переписать через рекурсию или `fold`.
|
||||
**Практически — не стоит.**
|
||||
|
||||
Rust — прагматичный язык. Иногда **императивный код лучше**:
|
||||
1. **Сложная мутация состояния**: Если тебе нужно обновлять 5 разных переменных в зависимости от сложной логики на каждом шаге, `fold` превратится в ад с кортежем из 5 элементов. Обычный `for` будет чище.
|
||||
2. **Ранний выход из вложенных циклов**: Выйти из тройного вложенного цикла через `break 'label` проще, чем писать цепочку итераторов, которая умеет прерываться.
|
||||
3. **Графы и произвольный доступ**: Итераторы хороши для *последовательностей*. Если ты прыгаешь по индексам массива туда-сюда (`i`, `i+5`, `i/2`), функциональный стиль будет выглядеть как натягивание совы на глобус.
|
||||
|
||||
**Совет:** Используй итераторы для *трансформации потоков данных* (фильтрация, маппинг, поиск, агрегация). Используй циклы для *сложного управления потоком выполнения* или когда состояние слишком запутанное. В Rust нормально сочетать оба подхода.
|
||||
Loading…
Add table
Add a link
Reference in a new issue