如何用边界值做一个'for'循环,并作为浮点值来执行?
我需要实现一个for
循环,该循环从一个浮点数转到另一个浮点数。如何用边界值做一个'for'循环,并作为浮点值来执行?
我知道如何实现,在C类语言:
for (float i = -1.0; i < 1.0; i += 0.01) { /* ... */ }
我也知道,在鲁斯特我可以指定使用step_by
循环一步,这给了我我想要的,如果我有边界价值观和步为整数:
#![feature(iterator_step_by)] fn main() {
for i in (0..30).step_by(3) {
println!("Index {}", i);
}
}
当我这样做,浮点数,它会导致编译错误:
#![feature(iterator_step_by)] fn main() {
for i in (-1.0..1.0).step_by(0.01) {
println!("Index {}", i);
}
}
这里是编译输出:
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`
我如何能实现这个循环中,锈?
回答:
如果你还没有,我请你读戈德堡的What Every Computer Scientist Should Know About Floating-Point Arithmetic。
与浮点的问题,是你的代码可以做200或201次迭代,这取决于循环的最后一个步骤是否结束是i = 0.99
或i = 0.999999
(这仍然是< 1
即使真的很近)。
为了避免这个脚枪,Rust不允许迭代f32
或f64
的范围。相反,它强迫你使用积分步骤:
for i in -100..100 { let i = i as f32 * 0.01;
// ...
}
回答:
作为一个真正的迭代器:
Playground
/// 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);
}
}
回答:
另一个答案使用迭代器,但在一个稍微不同的方式playground
extern crate num; use num::{Float, FromPrimitive};
fn linspace<T>(start: T, stop: T, nstep: u32) -> Vec<T>
where
T: Float + FromPrimitive,
{
let delta: T = (stop - start)/T::from_u32(nstep - 1).expect("out of range");
return (0..(nstep))
.map(|i| start + T::from_u32(i).expect("out of range") * delta)
.collect();
}
fn main() {
for f in linspace(-1f32, 1f32, 3) {
println!("{}", f);
}
}
在夜间,您可以使用conservative impl trait
功能,以避免Vec
分配playground
#![feature(conservative_impl_trait)] extern crate num;
use num::{Float, FromPrimitive};
fn linspace<T>(start: T, stop: T, nstep: u32) -> impl Iterator<Item = T>
where
T: Float + FromPrimitive,
{
let delta: T = (stop - start)/T::from_u32(nstep - 1).expect("out of range");
return (0..(nstep))
.map(move |i| start + T::from_u32(i).expect("out of range") * delta);
}
fn main() {
for f in linspace(-1f32, 1f32, 3) {
println!("{}", f);
}
}
以上是 如何用边界值做一个'for'循环,并作为浮点值来执行? 的全部内容, 来源链接: utcz.com/qa/262931.html