try_fold 是停止无限迭代的首选方法还是有更多惯用的替代方法?
Is try_fold the preferred way to stop an infinite iteration or are there more idiomatic alternatives?
我正在寻找中断无限迭代器迭代的方法。我发现 try_fold
accomplishes my objective. However, that requires doing the awkward thing of returning an Err
on the successful case. What I'm looking to understand is if this is an idiomatic way of doing things. The only other way I can think of doing this is using a regular for
, or something like a find
while keeping external state (which feels even weirder!). I know in clojure there's reduced
,但我找不到与 rust 对应的东西。
这是一个最小的可行示例。该示例围绕初始 Vec 循环,对每个项目进行求和,并在第一个大于 10 的总和处停止。即它 returns 12,因为 1 + 5 - 3 + 1 + 5 - 3 + 1 + 5 = 12
:
fn main() {
let seed = vec![1, 5, -3];
let res = seed.iter().cycle().try_fold(0, |accum, value| {
let next = accum + value;
if next > 10 {
Err(next)
} else {
Ok(next)
}
});
if let Err(res) = res {
println!("{:?}", res);
} else {
unreachable!();
}
}
让我觉得奇怪的部分是 if let Err(res) = res
是积极的条件,实际上是跳出循环的唯一途径(因此另一个分支无法到达)。
有'better'方法吗?
简单,正常迭代即可
fn main() {
let seed = vec![1, 5, -3];
let mut accum = 0;
for value in seed.iter().cycle() {
accum += value;
if accum > 10 {
break;
}
}
println!("{:?}", accum)
}
如果您不介意使用完善的外部库,请考虑使用 itertools::fold_while。如果您喜欢这种编程风格,可能还有其他有用的扩展。
您可以使用 scan
和 find
:
fn main() {
let seed = vec![1, 5, -3];
let res = seed
.iter()
.cycle()
.scan(0, |accum, value| {
*accum += value;
Some(*accum)
})
.find(|&x| x > 10)
.unwrap();
println!("{:?}", res);
}
这实际上比使用 itertools
中的 fold_while
稍微短一些,后者看起来像这样:
use itertools::{FoldWhile::{Continue, Done}, Itertools};
fn main() {
let seed = vec![1, 5, -3];
let res = seed
.iter()
.cycle()
.fold_while(0, |accum, value| {
let next = accum + value;
if next > 10 {
Done(next)
} else {
Continue(next)
}
})
.into_inner();
println!("{:?}", res);
}
到今天为止,您的问题还没有明显的答案,这真是令人惊讶。就像 @peter-hall 建议的那样,scan
是问题的惯用功能答案,但 Rust 的 scan
is known to be ugly to use.
在我看来 try_fold
仍然是最生锈的选择。这是我的综合建议,可以使环状折叠更令人满意 (选择一项或多项更改!) :
let res = {
use Result::{Err as Break, Ok as Next};
seed.iter()
.cycle()
.try_fold(0, |accum, value| match accum + value {
res if res > 10 => Break(res),
next => Next(next),
})
.unwrap_err()
};
在我看来,使用 match
比 if
/else
更有助于命名案例。 Result
类型别名还提供了一些描述中断缩进的语义。
如果不想用unwrap_err
,有人建议用
代替
let res = iter.try_fold(...).unwrap_or_else(|res| res);
也可以使用无可辩驳的模式:
let (Ok(res) | Err(res)) = iter.try_fold(...);
如果是我自己的代码,我会尽可能保持简单和普通:
let res = seed
.iter()
.cycle()
.try_fold(0, |accum, value| match accum + value {
res if res > 10 => Err(res),
acc => Ok(acc),
})
.unwrap_err();
阅读 .unwrap_err()
对我来说足够清楚了,因为这意味着 Err
是我们期望的 return 类型。这是我最好的一击,干杯!
我正在寻找中断无限迭代器迭代的方法。我发现 try_fold
accomplishes my objective. However, that requires doing the awkward thing of returning an Err
on the successful case. What I'm looking to understand is if this is an idiomatic way of doing things. The only other way I can think of doing this is using a regular for
, or something like a find
while keeping external state (which feels even weirder!). I know in clojure there's reduced
,但我找不到与 rust 对应的东西。
这是一个最小的可行示例。该示例围绕初始 Vec 循环,对每个项目进行求和,并在第一个大于 10 的总和处停止。即它 returns 12,因为 1 + 5 - 3 + 1 + 5 - 3 + 1 + 5 = 12
:
fn main() {
let seed = vec![1, 5, -3];
let res = seed.iter().cycle().try_fold(0, |accum, value| {
let next = accum + value;
if next > 10 {
Err(next)
} else {
Ok(next)
}
});
if let Err(res) = res {
println!("{:?}", res);
} else {
unreachable!();
}
}
让我觉得奇怪的部分是 if let Err(res) = res
是积极的条件,实际上是跳出循环的唯一途径(因此另一个分支无法到达)。
有'better'方法吗?
简单,正常迭代即可
fn main() {
let seed = vec![1, 5, -3];
let mut accum = 0;
for value in seed.iter().cycle() {
accum += value;
if accum > 10 {
break;
}
}
println!("{:?}", accum)
}
如果您不介意使用完善的外部库,请考虑使用 itertools::fold_while。如果您喜欢这种编程风格,可能还有其他有用的扩展。
您可以使用 scan
和 find
:
fn main() {
let seed = vec![1, 5, -3];
let res = seed
.iter()
.cycle()
.scan(0, |accum, value| {
*accum += value;
Some(*accum)
})
.find(|&x| x > 10)
.unwrap();
println!("{:?}", res);
}
这实际上比使用 itertools
中的 fold_while
稍微短一些,后者看起来像这样:
use itertools::{FoldWhile::{Continue, Done}, Itertools};
fn main() {
let seed = vec![1, 5, -3];
let res = seed
.iter()
.cycle()
.fold_while(0, |accum, value| {
let next = accum + value;
if next > 10 {
Done(next)
} else {
Continue(next)
}
})
.into_inner();
println!("{:?}", res);
}
到今天为止,您的问题还没有明显的答案,这真是令人惊讶。就像 @peter-hall 建议的那样,scan
是问题的惯用功能答案,但 Rust 的 scan
is known to be ugly to use.
在我看来 try_fold
仍然是最生锈的选择。这是我的综合建议,可以使环状折叠更令人满意 (选择一项或多项更改!) :
let res = {
use Result::{Err as Break, Ok as Next};
seed.iter()
.cycle()
.try_fold(0, |accum, value| match accum + value {
res if res > 10 => Break(res),
next => Next(next),
})
.unwrap_err()
};
在我看来,使用 match
比 if
/else
更有助于命名案例。 Result
类型别名还提供了一些描述中断缩进的语义。
如果不想用unwrap_err
,有人建议用
let res = iter.try_fold(...).unwrap_or_else(|res| res);
也可以使用无可辩驳的模式:
let (Ok(res) | Err(res)) = iter.try_fold(...);
如果是我自己的代码,我会尽可能保持简单和普通:
let res = seed
.iter()
.cycle()
.try_fold(0, |accum, value| match accum + value {
res if res > 10 => Err(res),
acc => Ok(acc),
})
.unwrap_err();
阅读 .unwrap_err()
对我来说足够清楚了,因为这意味着 Err
是我们期望的 return 类型。这是我最好的一击,干杯!