Fold
fold используется для превращения множества в 1 значение.
Базовый синтаксис
fn fold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,init: исходное значение счётчика;f: замыкание, которое тоже принимает 2 аргумента: счётчик и объект. Замыкание возвращает счётчик.
Как работает
fold() преобразовывает каждый элемент по цепочке :
- Начинает со значения
initв счётчике; - Для каждого элемента применяет замыкание
f(accumulator, element); - Возвращает финальное значение счётчика после обработки всех элементов. Наглядно по шагам:
fold(init, |acc, x| f(acc, x))
// Шаг 1: acc = init
// Шаг 2: acc = f(init, item1)
// Шаг 3: acc = f(acc, item2)
// Шаг 4: acc = f(acc, item3)
// ...
// Возврат финального acc
Сумма чисел:
let numbers = vec![1, 2, 3, 4, 5];
let sum = numbers.iter().fold(0, |acc, &x| acc + x);
println!("Sum: {}", sum); // 15
// По шагам:
// acc = 0
// acc = 0 + 1 = 1
// acc = 1 + 2 = 3
// acc = 3 + 3 = 6
// acc = 6 + 4 = 10
// acc = 10 + 5 = 15
// код выше можно заменить на sum()
Произведение чисел:
let numbers = vec![1, 2, 3, 4, 5];
let product = numbers.iter().fold(1, |acc, &x| acc * x);
println!("Product: {}", product); // 120
// код выше можно заменить на product()
Нахождение максимального значения
let numbers = vec![3, 1, 4, 1, 5, 9, 2];
let max = numbers.iter().fold(i32::MIN, |acc, &x| acc.max(x));
println!("Max: {}", max); // 9
Соединение (конкатенация) строк
Счётчик у fold необязательно числовой, можно использовать, например, String:
fn hello_world_concat() {
let words = vec!["hello", "world", "rust"];
let sentence = words.iter().fold(String::new(), |acc, &word| {
if acc.is_empty() {
word.to_string()
} else {
acc + " " + word
}});
println!("Sentence: '{}'", sentence); // "hello world rust"
}
fn giant_grunts(initial: char) -> String {
["Bee", "Fee", "Gee", "Fi", "Hi", "Fo", "Mo", "Fum", "Tum"].iter().fold(
String::new(),
|acc, grunt| if grunt.starts_with(initial) { acc + grunt } else { acc },
)}
fn main() {
let song = giant_grunts('F');
println!("{song:?}"); // "FeeFiFoFum"
}Разделение на чётные и нечётные
let numbers = vec![1, 2, 3, 4, 5];
let result = numbers.iter().fold(
(Vec::new(), Vec::new()), // (evens, odds)
|(mut evens, mut odds), &x| {
if x % 2 == 0 {
evens.push(x);
} else {
odds.push(x);
}
(evens, odds)
}
);
println!("Evens: {:?}, Odds: {:?}", result.0, result.1);
// Evens: [2, 4], Odds: [1, 3, 5]
Получение частоты символов в тексте:
use std::collections::HashMap;
pub fn first_non_repeating(s: &str) -> Option<HashMap<char, i32>> {
let char_frequency = s.chars().fold(HashMap::new(), |mut acc, char| {
*acc.entry(char).or_insert(0) += 1;
acc
});
Some(char_frequency)
}
fn main() {
println!("Letter frequency: {:?}", first_non_repeating("stress"));
} // Letter frequency: Some({'e': 1, 'r': 1, 's': 3, 't': 1})
Реализация других методов
Реализация map()
let numbers = vec![1, 2, 3, 4, 5];
let doubled: Vec<i32> = numbers.iter().fold(Vec::new(), |mut acc, &x| {
acc.push(x * 2);
acc });
println!("Doubled: {:?}", doubled); // [2, 4, 6, 8, 10]
Реализация filter()
let numbers = vec![1, 2, 3, 4, 5, 6];
let evens: Vec<i32> = numbers.iter().fold(Vec::new(), |mut acc, &x| {
if x % 2 == 0 {
acc.push(x); }
acc });
println!("Evens: {:?}", evens); // [2, 4, 6]
Вариация try_fold()
От fold() отличается тем, что прерывает выполнение и возвращает Result(Err):
let strings = ["1", "2", "3", "4", "5"];
// перевести строки в числа и суммировать их
let sum: Result<i32, _> = strings
.iter()
.try_fold(0, |acc, &s| match s.parse::<i32>() {
Ok(num) => Ok(acc + num),
Err(e) => Err(e),
});
match sum {
Ok(total) => println!("Total: {}", total), // 15
Err(e) => println!("Parse error: {}", e), }