3

I'm having trouble cloning a Map in a compact way:

extern crate itertools_num;

use itertools_num::linspace;

fn main() {
    // 440Hz as wave frequency (middle A)
    let freq: f64 = 440.0;
    // Time vector sampled at 880 times/s (~Nyquist), over 1s
    let delta: f64 = 1.0 / freq / 2.0;
    let time_1s = linspace(0.0, 1.0, (freq / 2.0) as usize)
        .map(|sample| { sample * delta});

    let sine_440: Vec<f64> = time_1s.map(|time_sample| {
        (freq * time_sample).sin()
    }).collect();

    let sine_100: Vec<f64> = time_1s.map(|time_sample| {
        (100.0 * time_sample).sin()
    }).collect();
}

The error I get with this code is

`time_1s` moved here because it has type `std::iter::Map<itertools_num::Linspace<f64>, [closure@examples/linear_dft.rs:12:14: 12:40 delta:&f64]>`, which is non-copyable

which is understandable, but if I try to use time_1s.clone() instead, I get

note: the method `clone` exists but the following trait bounds were not satisfied: `[closure@examples/linear_dft.rs:12:14: 12:40 delta:_] : std::clone::Clone`
error: the type of this value must be known in this context
     (freq * time_sample).sin()

which is also understandable, but storing (freq * time_sample).sin() in a let foo: f64 inside the closure before returning it doesn't have any effect.

What am I supposed to do in a situation like this? All I wanted to do was use the time vector more than once.

bright-star
  • 6,016
  • 6
  • 42
  • 81
  • The important error is the fact that the closure isn't clonable. http://stackoverflow.com/q/27883509/155423; http://stackoverflow.com/q/21933892/155423; http://stackoverflow.com/q/39803231/155423; http://stackoverflow.com/q/28011803/155423. – Shepmaster Oct 10 '16 at 12:43

2 Answers2

7

One way to use time_1s twice is to do both together and unzip at the end:

extern crate itertools_num;

use itertools_num::linspace;

fn main() {
    // 440Hz as wave frequency (middle A)
    let freq: f64 = 440.0;
    // Time vector sampled at 880 times/s (~Nyquist), over 1s
    let delta: f64 = 1.0 / freq / 2.0;
    let time_1s = linspace(0.0, 1.0, (freq / 2.0) as usize)
            .map(|sample| { sample * delta});

    let (sine_440, sine_100): (Vec<f64>, Vec<f64>) = time_1s.map(|time_sample| {
        ((freq * time_sample).sin(),
         (100.0 * time_sample).sin())
    }).unzip();
}
Chris Emerson
  • 13,041
  • 3
  • 44
  • 66
  • This is nice, I hadn't considered `unzip` at all. I wonder what allocations happen with this set up? I imagine it does not make a very big impact at all but I am still curious - I am not well versed on the internals of Rust iterators/enumerables. – Simon Whitehead Oct 10 '16 at 08:59
  • Accepting this answer because it has the functional formulation I was looking for. (Also it has more dots.) – bright-star Oct 10 '16 at 15:23
3

My original answer caused 3 enumerations of the iterator. Ideally you were looking for 2 iterations.

Since map consumes the iterator, it seems like the easier and more efficient way to do this without causing more iterations or clones than necessary is to just loop over it yourself only once:

let time_1s = linspace(0.0, 1.0, (freq / 2.0) as usize)
    .map(|sample| { sample * delta});

let mut sine_100 = Vec::new();
let mut sine_440 = Vec::new();

for time_sample in time_1s {
    sine_100.push((100.0 * time_sample).sin());
    sine_440.push((freq * time_sample).sin());
}

println!("{:?}", sine_100);
println!("{:?}", sine_440);
Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138