2

I was using the strategy from this question's answer, and I was interested in producing another sequence that is a function of the two being created by the iterator map:

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,
         summed_signal): (Vec<f64>, Vec<f64>, Vec<f64>) =
        time_1s.map(|time_sample| {
            let sample_440 = (freq * &time_sample).sin();
            let sample_100 = (100.0 * &time_sample).sin();
            let summed_sample = &sample_440 + &sample_100;

            (sample_440, sample_100, summed_sample)
        }).unzip();
}

Like the closure indicates, the third signal is the sum of the previous two. What's baffling is the error I get:

error[E0271]: type mismatch resolving `<[closure@src/main.rs:17:21: 23:10 freq:_] as std::ops::FnOnce<(f64,)>>::Output == (_, _)`
  --> src/main.rs:23:12
   |
23 |         }).unzip();
   |            ^^^^^ expected a tuple with 3 elements, found one with 2 elements
   |
   = note: expected type `(f64, f64, f64)`
   = note:    found type `(_, _)`
   = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Map<std::iter::Map<itertools_num::Linspace<f64>, [closure@src/main.rs:11:14: 11:40 delta:_]>, [closure@src/main.rs:17:21: 23:10 freq:_]>`

error[E0308]: mismatched types
  --> src/main.rs:17:9
   |
17 |         time_1s.map(|time_sample| {
   |         ^ expected a tuple with 3 elements, found one with 2 elements
   |
   = note: expected type `(std::vec::Vec<f64>, std::vec::Vec<f64>, std::vec::Vec<f64>)`
   = note:    found type `(_, _)`

I could understand a type error, but why should the third tuple item just be completely ignored?

Community
  • 1
  • 1
bright-star
  • 6,016
  • 6
  • 42
  • 81

3 Answers3

5

You can create custom unzip3 implementation based on the standard unzip:

trait IteratorUnzip3 {
    fn unzip3<A, B, C, FromA, FromB, FromC>(self) -> (FromA, FromB, FromC) where
        FromA: Default + Extend<A>,
        FromB: Default + Extend<B>,
        FromC: Default + Extend<C>,
        Self: Sized + Iterator<Item=(A, B, C)>,
    {
        let mut ts: FromA = Default::default();
        let mut us: FromB = Default::default();
        let mut vs: FromC = Default::default();

        for (t, u, v) in self {
            ts.extend(Some(t));
            us.extend(Some(u));
            vs.extend(Some(v));
        }

        (ts, us, vs)
    }
}

impl<A, B, C, T: Iterator<Item=(A, B, C)>> IteratorUnzip3 for T{}

fn main() { 
    let freq: f64 = 440.0; 

    let (sine_440, sine_100, summed_signal): (Vec<f64>, Vec<f64>, Vec<f64>) = 
        [1.0, 2.0, 3.0].iter().cloned()
            .map(|time_sample| { 
                let sample_440 = (freq * &time_sample).sin(); 
                let sample_100 = (100.0 * &time_sample).sin(); 
                let summed_sample = &sample_440 + &sample_100; 
                (sample_440, sample_100, summed_sample) 
            }).unzip3(); 

    println!("{:?}\n{:?}\n{:?}", sine_440, sine_100, summed_signal);
}

Playground

aSpex
  • 4,790
  • 14
  • 25
4

Iterator::unzip only supports 2-tuples. You can tell this from the trait bound Self: Iterator<Item=(A, B)> that appears in the where clause.

Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
4

Iterator::unzip is only defined to return a tuple of two items:

fn unzip<A, B, FromA, FromB>(self) -> (FromA, FromB) 
    where FromA: Default + Extend<A>, 
          FromB: Default + Extend<B>,
          Self: Iterator<Item=(A, B)>,

Note how the return type is (FromA, FromB) - a tuple with two values.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • I don't know why I didn't think to just look at the signature. I just assumed it could handle any tuple length. – bright-star Oct 11 '16 at 02:46
  • 2
    @TrevorAlexander: Unfortunately... no. Rust does not have variadics, so a number of useful methods are "stuck" until someone figures out how to implement them without variadics or variadics (maybe in a limited form) make it into the language. – Matthieu M. Oct 11 '16 at 06:49