2

How do I implement an apply_n_times function which gets a function f: T -> T and a number n and the result will be a function which applies f ntimes?

E.g. apply_n_times(f, 0) equals |x| x and apply_n_times(f, 3) equals |x| f(f(f(x))).

There is no deeper sense in this function, I just want to implement it for learning reasons.

My current code:

fn apply_n_times<T>(f: Fn(T) -> T, n: i32) -> dyn Fn(T) -> T {
    if n < 0 {
        panic!("Cannot apply less than 0 times!");
    }

    if n == 1 {
        |x: T| x
    } else {
        |x| f(apply_n_times(f, n - 1)(x))
    }
}

fn times_two(n: i32) -> i32 {
    return n * 2;
}

fn main() {
    println!("{}", apply_n_times(times_two, 0)(3));
    println!("{}", apply_n_times(times_two, 1)(3));
    println!("{}", apply_n_times(times_two, 3)(3));
}

I'm at chapter 13 of the Rust book, but I searched forward a bit. I probably have to return a Box, but I'm not really sure. I tried it and I failed.

I also wanted to change the signature to this, but this only results in problems:

fn apply_n_times<F, T>(f: F, n: i32) -> F
where
    F: Fn(T) -> T,

Unfortunately, the compiler errors do not help me; they say what's wrong at a low level, but I was running in a circle.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Kevin Meier
  • 2,339
  • 3
  • 25
  • 52

1 Answers1

10
fn apply_n_times<T>(f: impl Fn(T) -> T, n: usize) -> impl Fn(T) -> T {
    move |arg| (0..n).fold(arg, |a, _| f(a))
}

Using a usize avoids the need for the negative check. Consider using FnMut instead of Fn as it's more flexible for users of the function.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Thanks, especially for the references :)! – Kevin Meier Aug 05 '20 at 20:53
  • I think using `u32` or `u64` makes more sense here, since the length isn't used as a pointer offset or something similar and its size doesn't need to be platform dependent. – Aloso Aug 06 '20 at 02:47