我需要实现一个for从一个浮点数到另一个浮点数的循环,其中步长为另一个浮点数.
我知道如何用类C语言实现它:
for (float i = -1.0; i < 1.0; i += 0.01) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
我也知道在Rust中我可以使用指定循环步骤step_by,如果我有边界值并且步长为整数,那么它给了我想要的东西:
#![feature(iterator_step_by)]
fn main() {
for i in (0..30).step_by(3) {
println!("Index {}", i);
}
}
Run Code Online (Sandbox Code Playgroud)
当我使用浮点数时,它会导致编译错误:
#![feature(iterator_step_by)]
fn main() {
for i in (-1.0..1.0).step_by(0.01) {
println!("Index {}", i);
}
}
Run Code Online (Sandbox Code Playgroud)
这是编译输出:
error[E0599]: no method named `step_by` found for type `std::ops::Range<{float}>` in the current scope
--> src/main.rs:4:26
|
4 | for i in (-1.0..1.0).step_by(0.01) {
| ^^^^^^^
|
= note: the method `step_by` exists but the following trait bounds were not satisfied:
`std::ops::Range<{float}> : std::iter::Iterator`
`&mut std::ops::Range<{float}> : std::iter::Iterator`
Run Code Online (Sandbox Code Playgroud)
如何在Rust中实现此循环?
这基本上与已接受的答案相同,但您可能更喜欢写类似的内容:
for i in (-100..100).map(|x| x as f64 * 0.01) {
println!("Index {}", i);
}
Run Code Online (Sandbox Code Playgroud)
如果你还没有,我邀请你阅读Goldberg的每个计算机科学家应该知道的关于浮点运算的内容.
浮点的问题在于,您的代码可能正在进行200或201次迭代,具体取决于循环的最后一步是否结束i = 0.99或i = 0.999999(< 1即使非常接近).
为了避免这种情况footgun,锈不允许迭代一个范围f32或f64.相反,它会强制您使用整体步骤:
for i in -100..100 {
let i = i as f32 * 0.01;
// ...
}
Run Code Online (Sandbox Code Playgroud)
作为一个真正的迭代器:
/// produces: [ linear_interpol(start, end, i/steps) | i <- 0..steps ]
/// (does NOT include "end")
///
/// linear_interpol(a, b, p) = (1 - p) * a + p * b
pub struct FloatIterator {
current: u64,
current_back: u64,
steps: u64,
start: f64,
end: f64,
}
impl FloatIterator {
pub fn new(start: f64, end: f64, steps: u64) -> Self {
FloatIterator {
current: 0,
current_back: steps,
steps: steps,
start: start,
end: end,
}
}
/// calculates number of steps from (end - start) / step
pub fn new_with_step(start: f64, end: f64, step: f64) -> Self {
let steps = ((end - start) / step).abs().round() as u64;
Self::new(start, end, steps)
}
pub fn length(&self) -> u64 {
self.current_back - self.current
}
fn at(&self, pos: u64) -> f64 {
let f_pos = pos as f64 / self.steps as f64;
(1. - f_pos) * self.start + f_pos * self.end
}
/// panics (in debug) when len doesn't fit in usize
fn usize_len(&self) -> usize {
let l = self.length();
debug_assert!(l <= ::std::usize::MAX as u64);
l as usize
}
}
impl Iterator for FloatIterator {
type Item = f64;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.current_back {
return None;
}
let result = self.at(self.current);
self.current += 1;
Some(result)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let l = self.usize_len();
(l, Some(l))
}
fn count(self) -> usize {
self.usize_len()
}
}
impl DoubleEndedIterator for FloatIterator {
fn next_back(&mut self) -> Option<Self::Item> {
if self.current >= self.current_back {
return None;
}
self.current_back -= 1;
let result = self.at(self.current_back);
Some(result)
}
}
impl ExactSizeIterator for FloatIterator {
fn len(&self) -> usize {
self.usize_len()
}
//fn is_empty(&self) -> bool {
// self.length() == 0u64
//}
}
pub fn main() {
println!(
"count: {}",
FloatIterator::new_with_step(-1.0, 1.0, 0.01).count()
);
for f in FloatIterator::new_with_step(-1.0, 1.0, 0.01) {
println!("{}", f);
}
}
Run Code Online (Sandbox Code Playgroud)