In this blog post, we will explore higher order functions and some common algorithms in Rust.
A Higher order function is a function that takes in one or more functions as arguments and then returns a function or a value as its result. The most commonest higher order functions include
- Map
- Filter
- Reduce
In Rust, you can implement various common algorithms from functional programming using the language’s support for functional programming concepts such as closures, iterators, and higher-order functions.
The workflow for most of these higher order functions is that we have a collection or list of elements or an iterator and we are applying a function to each element in the list and returning either another transformed list or a value.
1. Mapping
Mapping involves applying a function to each element of a collection. In Rust, you can use the map
method on iterators.
fn main() { let numbers = vec![1, 2, 3, 4, 5]; let doubled: Vec<_> = numbers.iter().map(|&x| x * 2).collect(); println!("{:?}", doubled); // Prints: [2, 4, 6, 8, 10] }
2. Filtering
Filtering involves selecting elements from a collection based on a predicate. In Rust, you can use the filter
method on iterators.
fn main() { let numbers = vec![1, 2, 3, 4, 5]; let even_numbers: Vec<_> = numbers.iter().filter(|&x| x % 2 == 0).cloned().collect(); println!("{:?}", even_numbers); // Prints: [2, 4] }
3. Folding (Reduction)
Folding involves reducing a collection to a single value by applying a binary operation. In Rust, you can use the fold
method on iterators.
fn main() { let numbers = vec![1, 2, 3, 4, 5]; let sum: i32 = numbers.iter().fold(0, |acc, &x| acc + x); println!("{}", sum); // Prints: 15 }
4. Flat Mapping
Flat mapping involves applying a function that returns an iterator to each element of a collection and then flattening the results.
fn main() { let numbers = vec![1, 2, 3]; let flattened: Vec<_> = numbers.iter().flat_map(|&x| vec![x, x * 2]).collect(); println!("{:?}", flattened); // Prints: [1, 2, 2, 4, 3, 6] }
5. Finding
Finding involves searching for the first element in a collection that satisfies a predicate.
fn main() { let numbers = vec![1, 2, 3, 4, 5]; let first_even = numbers.iter().find(|&x| x % 2 == 0); println!("{:?}", first_even); // Prints: Some(2) }
6. Partitioning
Partitioning involves splitting a collection into two parts based on a predicate.
fn main() { let numbers = vec![1, 2, 3, 4, 5]; let (even, odd): (Vec<_>, Vec<_>) = numbers.iter().partition(|&x| x % 2 == 0); println!("Even: {:?}, Odd: {:?}", even, odd); // Prints: Even: [2, 4], Odd: [1, 3, 5] }
7. Higher-Order Functions
Higher-order functions are functions that take other functions as arguments or return functions as results. Here is an example of a higher-order function that applies an operation to each element of a vector.
fn apply_operation<F>(vector: Vec<i32>, operation: F) -> Vec<i32> where F: Fn(i32) -> i32, { vector.into_iter().map(operation).collect() } fn main() { let numbers = vec![1, 2, 3, 4, 5]; let doubled = apply_operation(numbers, |x| x * 2); println!("{:?}", doubled); // Prints: [2, 4, 6, 8, 10] }
8. Closures
Closures are anonymous functions that can capture variables from their surrounding environment.
fn main() { let multiplier = |x| x * 3; let result = multiplier(4); println!("{}", result); // Prints: 12 }
9. Pattern Matching
Pattern matching is a powerful feature in Rust that allows developers to destructure and match complex data structures.
fn match_example(value: Option<i32>) { match value { Some(x) => println!("Received a value: {}", x), None => println!("Received None"), } } fn main() { match_example(Some(5)); match_example(None); }
These are some of the higher order functions and common algorithms we have to know as a developer in Rust and any programming language in general.
You can also check out the video tutorial for it.
Thanks for your time
Jesus Saves
By Jesse E.Agbe(JCharis)