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

5
我想将实现Iterator<(A,B)>的对象的输出拆分为实现Iterator<A>Iterator<B>的两个对象。由于其中一个输出可能会迭代多次,因此我需要缓冲Iterator<(A,B)>的输出(因为我不能依赖于Iterator<(A,B)>可克隆)。问题在于迭代器可能是无限的,因此我不能简单地将迭代器的输出收集到两个缓冲区中并返回两个缓冲区上的迭代器。
所以似乎我需要维护AB对象的缓冲区,每当其中一个缓冲区为空时,我都将用Iterator<(A,B)>对象的样本填充它。这意味着我将需要两个可迭代的结构体,它们具有对输入迭代器的可变引用(因为它们都需要在输入上调用next()来填充缓冲区),这是不可能的。
那么,有没有安全的方法来完成这个任务?
1个回答

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

我假设您愿意在单个线程上保留这两个迭代器1,因此在这种情况下,我们需要一个RefCell。我们还需要能够从这两个句柄访问RefCell,这意味着存储&RefCell<...>Rc<RefCell<...>>。前者过于限制,因为它只允许我们在创建RefCell的堆栈帧中使用和以下的迭代器,而我们想要能够自由地传递迭代器,因此选择 Rc

总之,我们基本上将存储一个 Rc<RefCell<Iterator<(A,B)>>>,只是关于缓冲的问题。这里工作的正确工具是一个 RingBuf ,因为我们想要在前面和后面进行有效的推入/弹出操作。因此,我们要共享的东西(即在 RefCell 中)可能如下所示:
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 Iterator<A> for First<A,B,It> {
    fn next(&mut self) -> Option<A> {
        // ...
    }
}

实现`next`的第一步是通过`self.data.borrow_mut();`获取到对`SharedInner`的`&mut`引用。然后从中获取一个元素:检查正确的缓冲区,或者从`iter`中获取一个新元素(记得缓存剩余的`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
    })
})

文本翻译如下:

文档:RingBuf.pop_front, Option.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);
}

打印输出

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]

playpen.

有多种方式可以进行优化,例如在获取First时,仅在存在Second句柄时缓冲剩余的B

1 如果您想要在单独的线程中运行它们,只需使用Mutex替换RefCell,将Rc替换为Arc,并添加必要的限制。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接