How do I create a non-recursive calculation of factorial using iterators and ranges?
Asked Answered
E

2

10

I ran into a Rustlings exercise that keeps bugging me:

pub fn factorial(num: u64) -> u64 {
    // Complete this function to return factorial of num
    // Do not use:
    // - return
    // For extra fun don't use:
    // - imperative style loops (for, while)
    // - additional variables
    // For the most fun don't use:
    // - recursion
    // Execute `rustlings hint iterators4` for hints.
}

A hint to solution tells me...

In an imperative language you might write a for loop to iterate through multiply the values into a mutable variable. Or you might write code more functionally with recursion and a match clause. But you can also use ranges and iterators to solve this in rust.

I tried this approach, but I am missing something:

if num > 1 {
    (2..=num).map(|n| n * ( n - 1 ) ??? ).???
} else {
    1
}

Do I have to use something like .take_while instead of if?

Extempore answered 24/3, 2020 at 16:53 Comment(5)
Look at Iterator::foldHightension
if num > 1 { (2..=num).fold(1, |acc, n| acc * n) } else { 1 }Extempore
works perfect with fold! thx for that hintExtempore
You don't even need to special case <= 1 with this approach.Hightension
Well... That's true and I didn't even realize it... Rust is still pretty magical to me ;DExtempore
S
32

The factorial is defined as the product of all the numbers from a starting number down to 1. We use that definition and Iterator::product:

fn factorial(num: u64) -> u64 {
    (1..=num).product()
}

If you look at the implementation of Product for the integers, you'll see that it uses Iterator::fold under the hood:

impl Product for $a {
    fn product<I: Iterator<Item=Self>>(iter: I) -> Self {
        iter.fold($one, Mul::mul)
    }
}

You could hard-code this yourself:

fn factorial(num: u64) -> u64 {
    (1..=num).fold(1, |acc, v| acc * v)
}

See also:

Shikari answered 24/3, 2020 at 17:24 Comment(1)
WOW! How did I miss that one?? Thanx A LotExtempore
F
3

Although using .product() or .fold() is probably the best answer, you can also use .for_each().

fn factorial(num: u64) -> u64 {
    let mut x = 1;
    (1..=num).for_each(|i| x *= i);
    x
}
Flog answered 8/7, 2021 at 21:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.