将 Iterator<(A,B)> 拆分为 Iterator<A> 和 Iterator<B>

awe*_*kie 5 rust

我想将实现的对象的输出拆分Iterator<(A,B)>为两个实现Iterator<A>和 的对象Iterator<B>。由于其中一个输出可以比另一个迭代更多,我需要缓冲输出Iterator<(A,B)>(因为我不能依赖可Iterator<(A,B)>克隆。)问题是迭代器可能是无限的,所以我可以不是简单地将迭代器的输出收集到两个缓冲区中并在两个缓冲区上返回迭代器。

因此,似乎我需要保存AB对象的缓冲区,并且每当缓冲区之一为空时,我都会用来自Iterator<(A,B)>对象的样本填充它。这意味着我需要两个具有对输入迭代器的可变引用的可迭代结构(因为它们都需要调用next()输入来填充缓冲区),这是不可能的。

那么,有没有办法以安全的方式实现这一点?

huo*_*uon 5

这个有可能。正如您所确定的,您需要从两个句柄对基本迭代器进行可变引用,这可以使用具有“内部可变性”的类型,即在unsafe内部使用代码来公开用于获取&mut可别名数据的安全 API (即包含在a &) 通过动态强制执行编译器通常在外部编译时强制执行的不变量unsafe

我假设您很乐意将两个迭代器保留在单个线程1 上,因此,在这种情况下,我们需要一个RefCell. 我们还需要能够RefCell从两个句柄访问,需要存储 a&RefCell<...>Rc<RefCell<...>>。前者限制性太强,因为它只允许我们在RefCell创建的堆栈帧中和下方使用这对迭代器,而我们希望能够自由地传递迭代器,所以Rc它是。

总之,我们基本上要存储一个Rc<RefCell<Iterator<(A,B)>>>,只是缓冲的问题。此处工作的正确工具是 a,RingBuf因为我们希望在正面和背面进行高效的推送/弹出。因此,我们共享的内容(即在 中RefCell)可能如下所示:

struct SharedInner<A, B, It> {
    iter: It,
    first: RingBuf<A>,
    second: RingBuf<B>,
}
Run Code Online (Sandbox Code Playgroud)

我们可以将实际共享的类型缩写为type Shared<A, B, It> = Rc<RefCell<SharedInner<A, B, It>>>;,这允许我们定义迭代器:

struct First<A, B, It> {
    data: Shared<A, B, It>
}
impl Iterator<A> for First<A,B,It> {
    fn next(&mut self) -> Option<A> {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

为了实现next做的第一件事就是获得一个&mutSharedInner,通过self.data.borrow_mut();。然后从中取出一个元素:检查正确的缓冲区,或者从中获取一个新元素iter(记住缓冲 left-over B):

let mut inner = self.data.borrow_mut();

inner.first.pop_front().or_else(|| {
    inner.iter.next().map(|(a,b)| {
        inner.second.push(b);
        a
    })
})
Run Code Online (Sandbox Code Playgroud)

文档:RingBuf.pop_frontOption.or_else

另一侧的迭代器类似。总共:

use std::cell::RefCell;
use std::collections::{Deque, RingBuf};
use std::rc::Rc;

struct SharedInner<A, B, It> {
    iter: It,
    first: RingBuf<A>,
    second: RingBuf<B>
}

type Shared<A, B, It> = Rc<RefCell<SharedInner<A, B, It>>>;

struct First<A, B, It> {
    data: Shared<A, B, It>
}

impl<A,B, It: Iterator<(A,B)>> Iterator<A> for First<A, B, It> {
    fn next(&mut self) -> Option<A> {
        let mut inner = self.data.borrow_mut();

        // try to get one from the stored data
        inner.first.pop_front().or_else(|| 
            // nothing stored, we need a new element.
            inner.iter.next().map(|(a, b)| {
                inner.second.push(b);
                a
            }))
    }
}

struct Second<A, B, It> {
    data: Shared<A, B, It>
}
impl<A,B, It: Iterator<(A,B)>> Iterator<B> for Second<A,B,It> {
    fn next(&mut self) -> Option<B> {
        let mut inner = self.data.borrow_mut();

        inner.second.pop_front().or_else(|| {
            inner.iter.next().map(|(a, b)| {
                inner.first.push(a);
                b
            })
        })
    }
}

fn split<A, B, It: Iterator<(A,B)>>(it: It) -> (First<A, B, It>, 
                                                Second<A, B, It>) {
    let data = Rc::new(RefCell::new(SharedInner { 
        iter: it,
        first: RingBuf::new(),
        second: RingBuf::new(),
    }));

    (First { data: data.clone() }, Second { data: data })
}

fn main() {
    let pairs = range(1u32, 10 + 1).map(|x| (x, 1.0 / x as f64));

    let (mut first, mut second) = split(pairs);

    println!("first:");
    for x in first.by_ref().take(3) {
        println!("  {}", x);
    }

    println!("second:");
    for y in second.by_ref().take(5) {
        if y < 0.2 { break }
        println!("  {}", y);
    }

    let a = first.collect::<Vec<u32>>();
    let b = second.collect::<Vec<f64>>();

    println!("a {}\nb {}", a, b);
}
Run Code Online (Sandbox Code Playgroud)

哪个打印

first:
  1
  2
  3
second:
  1
  0.5
  0.333333
  0.25
  0.2
a [4, 5, 6, 7, 8, 9, 10]
b [0.166667, 0.142857, 0.125, 0.111111, 0.1]
Run Code Online (Sandbox Code Playgroud)

围栏

有多种方法可以对此进行优化,例如在获取 in 时FirstB如果Second存在句柄,则仅缓冲剩余部分。

1如果你希望在不同的线程中运行他们刚刚更换RefCellMutexRcArc,并添加必要的范围。