【问题标题】:How to create a function that creates a Cartesian product Iterator from an Iterator of Iterators?如何创建一个从迭代器的迭代器创建笛卡尔积迭代器的函数?
【发布时间】:2018-05-20 00:24:34
【问题描述】:

如果我想在 Haskell 中创建列表列表的笛卡尔积,我可以这样做:

product [] = [[]]
product (xs:xss) = concatMap (\k -> map (k:) (product1 xss)) xs

甚至这个:

sequence xss

我正在尝试实现一个高效的迭代器,它可以在 Rust 中做同样的事情,但我不确定我的尝试有什么问题:

use std::iter::{empty, once};

fn product<T, I, V>(xss: I) -> Box<Iterator<Item = Iterator<Item = T>>>
where
    T: Clone,
    V: IntoIterator<Item = T>,
    I: IntoIterator<Item = V>,
{
    Box::new(xss.into_iter().fold(once(empty()), |acc, xs| {
        xs.into_iter().flat_map(|x| acc.map(|ys| ys.chain(once(x))))
    }))
}

fn main() {
    let data = vec![[1, 2, 3], [10, 20, 30], [100, 200, 300]];
    let it: Vec<Vec<u32>> = product(data).collect();
    println!("{:?}", it);
}

(playground)

产生这些错误:

error[E0308]: mismatched types
  --> src/main.rs:10:9
   |
10 |         xs.into_iter().flat_map(|x| acc.map(|ys| ys.chain(once(x))))
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::iter::Once`, found struct `std::iter::FlatMap`
   |
   = note: expected type `std::iter::Once<std::iter::Empty<T>>`
              found type `std::iter::FlatMap<<V as std::iter::IntoIterator>::IntoIter, std::iter::Map<std::iter::Once<std::iter::Empty<T>>, [closure@src/main.rs:10:45: 10:67 x:_]>, [closure@src/main.rs:10:33: 10:68 acc:_]>`

error[E0271]: type mismatch resolving `<std::iter::Once<std::iter::Empty<T>> as std::iter::Iterator>::Item == std::iter::Iterator<Item=T>`
  --> src/main.rs:9:5
   |
9  | /     Box::new(xss.into_iter().fold(once(empty()), |acc, xs| {
10 | |         xs.into_iter().flat_map(|x| acc.map(|ys| ys.chain(once(x))))
11 | |     }))
   | |_______^ expected struct `std::iter::Empty`, found trait std::iter::Iterator
   |
   = note: expected type `std::iter::Empty<T>`
              found type `std::iter::Iterator<Item=T>`
   = note: required for the cast to the object type `std::iter::Iterator<Item=std::iter::Iterator<Item=T>>`

error[E0277]: the trait bound `[{integer}; 3]: std::iter::Iterator` is not satisfied
  --> src/main.rs:16:29
   |
16 |     let it: Vec<Vec<u32>> = product(data).collect();
   |                             ^^^^^^^ `[{integer}; 3]` is not an iterator; maybe try calling `.iter()` or a similar method
   |
   = help: the trait `std::iter::Iterator` is not implemented for `[{integer}; 3]`
   = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `[{integer}; 3]`
   = note: required by `product`

error: the `collect` method cannot be invoked on a trait object
  --> src/main.rs:16:43
   |
16 |     let it: Vec<Vec<u32>> = product(data).collect();
   |                                           ^^^^^^^

第一个错误让我感觉 Rust 甚至无法使用 fold 创建一个惰性消耗的迭代器,因为 Empty&lt;T&gt; 是一个 Iterator&lt;Item = T&gt;(至少在概念上),但我希望我错了。

【问题讨论】:

  • 盒装迭代器不能充分替代懒惰评估的 Haskell 变量,因为它不能倒带或克隆,因此这种直接翻译将不起作用。将列表表示为一串带框的Chains 也不会有效。
  • @red75prime 好的,那么我该如何在通用和功能风格上做到这一点?
  • 你正在写 Rust,但想到 Haskell,它会出错。看看this,看看 rust 实现会是什么样子。
  • @user1685095,您需要实现隐藏在引擎盖下的所有机器功能语言。我gave it a try.
  • 我在reddit上发布了这个问题的链接,得到了非常有趣的答案:reddit.com/r/rust/comments/bdlna5/…

标签: rust cartesian-product generic-collections


【解决方案1】:

对于它的价值,Itertools crate 实现了一个可行的笛卡尔积函数:

use itertools::Itertools;

let it = (0..2).cartesian_product("αβ".chars());
itertools::assert_equal(it, vec![(0, 'α'), (0, 'β'), (1, 'α'), (1, 'β')]);

【讨论】:

    【解决方案2】:

    您的方法注定要失败的第一个原因是因为您试图将设计用于处理列表的算法转换为处理迭代器的算法。列表适用于函数式方法,而迭代器则不适用,因为它们有状态。 next(&amp;mut self) 函数每次使用相同的参数调用时都不会返回相同的值,而 next(x:xs) 函数会。这就是implementation found in itertools 克隆迭代器的原因:保存它们的初始状态并在集合的下一次迭代中恢复它。

    第二个原因,即错误消息背后的原因,是您正在与 Rust 的类型系统作斗争。您对迭代器函数(foldflat_map 等)的所有调用的结果值不是特征对象,而是“具体类型”。例如iterator.fold(init, fn) 的结果类型是init 的类型。这就是为什么当你传递 fold 一个不返回 std::iter::Empty&lt;T&gt; 的 lambda 时编译器会抱怨。

    但情况会变得更糟。您可以想象将std::iter::Empty&lt;T&gt; 强制或强制转换为特征对象。唉,对象安全是必需的。简而言之,"A good intuition is “except in special circumstances, if your trait’s method uses Self, it is not object-safe."。但是迭代器的主要方法是next(&amp;mut self)

    【讨论】:

    • 对象安全在这里没有发挥作用 — it's fine to create a trait object from iter::empty。那个“直觉”指的是self以外的论点。
    • “您正试图将设计用于处理列表的算法转换为处理迭代器的算法”:这不是真的...... Haskell 列表更接近可克隆迭代器而不是它们生锈的 Vec。此处介绍的算法仅依赖于从迭代器中获取下一个元素并对其进行克隆。让这个两行代码的 Haskell 程序难以在 rust 中实现的原因是 Haskell 的惰性。
    猜你喜欢
    • 2023-03-09
    • 2016-07-24
    • 1970-01-01
    • 1970-01-01
    • 2021-10-15
    • 2012-05-29
    • 1970-01-01
    • 2010-10-09
    • 2017-09-28
    相关资源
    最近更新 更多