在 Rust 的任一方向上动态创建一个范围
Dynamically create a range in either direction in Rust
我正在学习 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 and some old ,基本上可以根据需要使用 (a..b).rev()
。
我的问题是,如何以动态方式执行此操作?如果我像这样使用 if...else
let all_x = if p1.x < p2.x { (p1.x..=p2.x) } else { (p2.x..=p1.x).rev() };
我收到类型错误,因为 else
与 if
不同
|
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<Item = i32>
、let all_x: dyn Iterator<Item = i32>
等上尝试了一系列不同的变体之后,我设法做到这一点的唯一方法是将它们变成集合,然后再返回迭代器。
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
结构。我无法让我的示例与 x1
、x2
等一起使用。对于不同的 post 可能是不同的问题 lol。
您可以动态调度它。将它们包装成 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))
}
}
枚举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<_>>());
}
我正在学习 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 and some old (a..b).rev()
。
我的问题是,如何以动态方式执行此操作?如果我像这样使用 if...else
let all_x = if p1.x < p2.x { (p1.x..=p2.x) } else { (p2.x..=p1.x).rev() };
我收到类型错误,因为 else
与 if
|
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<Item = i32>
、let all_x: dyn Iterator<Item = i32>
等上尝试了一系列不同的变体之后,我设法做到这一点的唯一方法是将它们变成集合,然后再返回迭代器。
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
结构。我无法让我的示例与 x1
、x2
等一起使用。对于不同的 post 可能是不同的问题 lol。
您可以动态调度它。将它们包装成 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))
}
}
枚举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<_>>());
}