【问题标题】:Dynamically create a range in either direction in Rust在 Rust 中动态创建任一方向的范围
【发布时间】:2022-01-11 06:41:27
【问题描述】:

我正在学习 Rust,最近进行了一项练习,我必须遍历可能朝任一方向发展的数字。我尝试了以下方法,结果出乎意料。

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Point {
    x: i32,
    y: i32
}

fn test() {
    let p1 = Point { x: 1, y: 8 };
    let p2 = Point { x: 3, y: 6 };

    let all_x = p1.x..=p2.x;
    println!("all_x: {:?}", all_x.clone().collect::<Vec<i32>>());
    let all_y = p1.y..=p2.y;
    println!("all_y: {:?}", all_y.clone().collect::<Vec<i32>>());
    
    let points: Vec<Point> = all_x.zip(all_y).map(|(x, y)| Point { x, y }).collect();

    println!("points: {:?}", points);
}

输出是

all_x: [1, 2, 3]
all_y: []
points: []

经过一番谷歌搜索,我找到了一个explanation 和一些old answers,它们基本上相当于根据需要使用(a..b).rev()

我的问题是,我如何以动态方式执行此操作?如果我像这样使用if...else

let all_x = if p1.x < p2.x { (p1.x..=p2.x) } else { (p2.x..=p1.x).rev() };

我收到类型错误,因为 elseif 不同

   |
58 |       let all_x = if p1.x < p2.x { (p1.x..=p2.x) }
   |                   -                ------------- expected because of this
   |  _________________|
   | |
59 | |     else { (p2.x..=p1.x).rev() };
   | |____________^^^^^^^^^^^^^^^^^^^_- `if` and `else` have incompatible types
   |              |
   |              expected struct `RangeInclusive`, found struct `Rev`
   |
   = note: expected type `RangeInclusive<_>`
            found struct `Rev<RangeInclusive<_>>`

在对let all_x: dyn Range&lt;Item = i32&gt;let all_x: dyn Iterator&lt;Item = i32&gt; 等尝试了一系列不同的变体之后,我设法做到这一点的唯一方法是将它们转换为集合,然后再返回迭代器。

let all_x: Vec<i32>;
if p1.x < p2.x { all_x = (p1.x..=p2.x).collect(); }
else { all_x = (p2.x..=p1.x).rev().collect(); }
let all_x = all_x.into_iter();
println!("all_x: {:?}", all_x.clone().collect::<Vec<i32>>());

let all_y: Vec<i32>;
if p1.y < p2.y { all_y = (p1.y..=p2.y).collect(); }
else { all_y = (p2.y..=p1.y).rev().collect(); }
let all_y = all_y.into_iter();
println!("all_y: {:?}", all_y.clone().collect::<Vec<i32>>());

提供期望的结果

all_x: [1, 2, 3]
all_y: [8, 7, 6]
points: [Point { x: 1, y: 8 }, Point { x: 2, y: 7 }, Point { x: 3, y: 6 }]

但有点重复,不雅,我假设在大量时效率不是很高。有没有更好的方法来处理这种情况?

注意:很抱歉包含 Point 结构。我无法让我的示例与 x1x2 等一起使用。对于不同的帖子可能会出现不同的问题,哈哈。

【问题讨论】:

  • 代码出现嘿嘿:)
  • @Netwave 哈哈,你找到我了。我试图从我的示例中尽可能多地删除这些内容,但时间不会说谎。

标签: rust iterator range


【解决方案1】:

您可以动态调度它。将它们包装成 Box 并返回一个动态对象,在本例中为 Iterator。例如:

fn maybe_reverse_range(init: usize, end: usize, reverse: bool) -> Box<dyn Iterator<Item=usize>> {
    if reverse {
        Box::new((init..end).rev())
    } else {
        Box::new((init..end))
    }
}

Playground

【讨论】:

  • 谢谢!我应该更多地关注 The Book 中的Box。对于我的情况,我制作了一个基于initend 自动翻转这些的版本,但这是正确的。
【解决方案2】:

枚举itertools::Either可用于解决if/else语句中的类型不兼容错误。像下面get_range_iter这样的函数使用Either可以减少代码重复。

use itertools::Either;
fn get_range_iter(start: i32, end: i32) -> impl Iterator<Item=i32> {
    if start < end {
        Either::Left(start..=end)
    } else {
        Either::Right((end..=start).rev())
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Point {
    x: i32,
    y: i32
}

fn main() {
    let p1 = Point { x: 1, y: 8 };
    let p2 = Point { x: 3, y: 6 };

    let all_x = get_range_iter(p1.x, p2.x);
    let all_y = get_range_iter(p1.y, p2.y);

    println!("all_x: {:?}", all_x.collect::<Vec<_>>());
    println!("all_y: {:?}", all_y.collect::<Vec<_>>());

}

Playground

【讨论】:

    猜你喜欢
    • 2012-03-05
    • 2021-11-29
    • 2013-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-27
    相关资源
    最近更新 更多