Могу ли я расширить итератор значениями, созданными внутри функции? - PullRequest
1 голос
/ 30 марта 2019

У меня есть программа , которая выполняет операцию scan со своим аргументом:

struct A(usize);
struct B(usize);

fn scan_something<'a>(xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
    let accum = 0;

    xs.iter().scan(accum, |accum, x| {
        *accum += x.0;
        Some(B(*accum))
    })
}

Я хочу расширить итератор несколькими значениями, сгенерированными внутри функции:

fn scan_something<'a>(xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
    let accum = 0;
    let head: A = A(xs.len());

    use std::iter::once;
    once(head).chain(xs.iter()).scan(accum, |accum, x| {
        *accum += x.0;
        Some(B(*accum))
    })
}

Это не компилируется, потому что once(head) является итератором A, в то время как xs.iter() является итератором &A.

Я могу реализовать Clone для A и поставьте .cloned() после xs.iter(), чтобы исправить это, но я не хочу клонировать весь xs, потому что он может быть очень длинным, а A в реальной программе не дешево клонировать.

Я ищу способ превратить once(head) в итератор &A, но не могу найти какой-либо метод.

Можно ли заставить его работать?

Ответы [ 2 ]

2 голосов
/ 30 марта 2019

Можно ли расширить итератор значениями, созданными внутри функции?

Да:

fn example<'a>(input: impl Iterator<Item = i32> + 'a) -> impl Iterator<Item = i32> + 'a {
    Some(42).into_iter().chain(input).chain(Some(99))
}

fn main() {
    for i in example(vec![1, 2, 3].into_iter()) {
        println!("{}", i);
    }
}

Я ищу способ поворотаonce(head) в итератор &A

Возьмите ссылку на значение:

iter::once(&head)

Возможно ли заставить [этот конкретный код] работать?

Нет.Компилятор даже скажет вам, что:

error[E0515]: cannot return value referencing local variable `head`
  --> src/lib.rs:10:5
   |
10 |       iter::once(&head).chain(xs.iter()).scan(accum, |accum, x| {
   |       ^          ----- `head` is borrowed here
   |  _____|
   | |
11 | |         *accum += x.0;
12 | |         Some(B(*accum))
13 | |     })
   | |______^ returns a value referencing data owned by the current function

См. Также:

Возможно ли заставить [что-то, близкое к этому коду] работать?

Может быть.Поскольку scan начинается со значения аккумулятора, вы можете просто использовать его вместо того, чтобы вставлять его в итератор:

fn scan_something<'a>(xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
    xs.iter().scan(xs.len(), |accum, x| {
        *accum += x.0;
        Some(B(*accum))
    })
}

Это будет означать, что в полученном итераторе будет на один элемент меньше.Если это приемлемо, зависит от вашего использования.

Более сложное решение состоит в том, чтобы иметь перечисление, которое представляет либо заемную, либо собственную стоимость.Затем вы можете создать итератор этих перечислений из входных данных и локального значения.Владение локальным значением передается возвращаемому итератору:

struct A(usize);
struct B(usize);

use std::iter;

// `A` doesn't implement `Clone`; if it did, use `Cow`
enum OwnedOrBorrowed<'a, T> {
    Owned(T),
    Borrowed(&'a T),
}

impl<'a, T> std::ops::Deref for OwnedOrBorrowed<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        match self {
            OwnedOrBorrowed::Owned(t) => t,
            OwnedOrBorrowed::Borrowed(t) => t,
        }
    }
}

fn scan_something<'a>(xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
    let accum = 0;
    let head = OwnedOrBorrowed::Owned(A(xs.len()));

    let borrowed = xs.iter().map(OwnedOrBorrowed::Borrowed);

    iter::once(head).chain(borrowed).scan(accum, |accum, x| {
        *accum += x.0;
        Some(B(*accum))
    })
}

Это не бесплатно - каждый вызов закрытия scan будет выполнять условную логику для проверки, является ли значение владельцем или заимствованным..

См. Также:

0 голосов
/ 30 марта 2019

Вне функции :

fn main() {
    let a: &[A] = &[A(1), A(2), A(3)];
    let b: &[A] = &[A(a.len())];
    for s in scan_something(b, a) {
        println!("{:?}", s);
    }
}

fn scan_something<'a>(xs1: &'a [A], xs: &'a [A]) -> impl Iterator<Item = B> + 'a {
    let iter3 = xs1.iter().chain(xs.iter());
    let accum = 0;
    iter3.scan(accum, |accum, x| {
        *accum += x.0;
        Some(B(*accum))
    })
}

#[derive(Debug)]
struct A(usize);

#[derive(Debug)]
struct B(usize);

Выход:

B(3)
B(4)
B(6)
B(9)
...