如何获取包含浮点数的迭代器的最小值或最大值?
How do I get the minimum or maximum value of an iterator containing floating point numbers?
我理解 why the floats don't have an implementation for Ord
但当我想偷懒并使用迭代器时,这对我没有特别帮助。
是否有解决方法或简单的方法来获取包含浮点数的迭代器的最小值/最小值/min_by?
我知道可以排序(这很慢)或将其包装在另一种类型中并实现所需的交易(这很冗长),但我希望有一些更优雅的东西。
浮点数有自己的 min
and max
方法来一致地处理 NaN,因此您可以折叠迭代器:
use std::f64;
fn main() {
let x = [2.0, 1.0, -10.0, 5.0, f64::NAN];
let min = x.iter().fold(f64::INFINITY, |a, &b| a.min(b));
println!("{}", min);
}
打印 -10
。
如果你想要不同的 NaN 处理,你可以使用 PartialOrd::partial_cmp
。例如,如果您希望传播 NaN,请折叠:
use std::f64;
use std::cmp::Ordering;
fn main() {
let x = [2.0, 1.0, -10.0, 5.0, f64::NAN];
let min = x.iter().fold(f64::INFINITY, |a, &b| {
match PartialOrd::partial_cmp(&a, &b) {
None => f64::NAN,
Some(Ordering::Less) => a,
Some(_) => b,
}
});
println!("{}", min);
}
大概是这样的?
fn main() {
use std::cmp::Ordering;
let mut x = [2.0, 1.0, -10.0, 5.0];
x.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
println!("min in x: {:?}", x);
}
我遇到的一件事是 sort_by
就地改变向量,所以你不能直接在链中使用它。
如果您知道您的数据不包含 NaN,则通过展开比较来断言这一事实:
fn example(x: &[f64]) -> Option<f64> {
x.iter()
.cloned()
.min_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
}
如果您的数据可能有 NaN,您需要专门处理这种情况。一种解决方案是说 all 16,777,214 NaN values 彼此相等,并且总是大于或小于其他数字:
use std::cmp::Ordering;
fn example(x: &[f64]) -> Option<f64> {
x.iter()
.cloned()
.min_by(|a, b| {
// all NaNs are greater than regular numbers
match (a.is_nan(), b.is_nan()) {
(true, true) => Ordering::Equal,
(true, false) => Ordering::Greater,
(false, true) => Ordering::Less,
_ => a.partial_cmp(b).unwrap(),
}
})
}
有许多可用的 crate 可用于为您提供代码所需的任何语义。
您应该不使用partial_cmp(b).unwrap_or(Ordering::Equal)
,因为当存在 NaN 时它会提供不稳定的结果,但它会导致 reader 认为它们已被处理:
use std::cmp::Ordering;
use std::f64;
fn example(x: &[f64]) -> Option<f64> {
x.iter()
.cloned()
.min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
}
fn main() {
println!("{:?}", example(&[f64::NAN, 1.0]));
println!("{:?}", example(&[1.0, f64::NAN]));
}
Some(NaN)
Some(1.0)
A built-in total-ordering 名为 .total_cmp()
is available on nighty, and should be on stable within a couple months, baring any surprising issues. (The vote to stablize the feature recently passed 的浮点数比较方法。)这实现了 IEEE 754 中定义的总排序,每个可能的 f64 位值都是明确排序,包括正零和负零,以及所有可能的 NaN。请注意,有些 NaN 排序在 Infinity 之上,有些 NaN 排序在 -Infinity 以下,因此在存在 NaN 的情况下“最大值”可能会造成混淆,但它会保持一致。
Floats 仍然不会实现 Ord
,因此它们不会直接排序,但样板已被缩减为一行,没有任何外部导入或恐慌的机会:
#![feature(total_cmp)]
fn main() {
let mut a: Vec<f64> = vec![2.0, 2.5, -0.5, 1.0, 1.5];
let maximum = *a.iter().max_by_key(f64::total_cmp).unwrap();
println!("The maximum value was {maximum}.");
a.sort_by(f64::total_cmp);
}
我理解 why the floats don't have an implementation for Ord
但当我想偷懒并使用迭代器时,这对我没有特别帮助。
是否有解决方法或简单的方法来获取包含浮点数的迭代器的最小值/最小值/min_by?
我知道可以排序(这很慢)或将其包装在另一种类型中并实现所需的交易(这很冗长),但我希望有一些更优雅的东西。
浮点数有自己的 min
and max
方法来一致地处理 NaN,因此您可以折叠迭代器:
use std::f64;
fn main() {
let x = [2.0, 1.0, -10.0, 5.0, f64::NAN];
let min = x.iter().fold(f64::INFINITY, |a, &b| a.min(b));
println!("{}", min);
}
打印 -10
。
如果你想要不同的 NaN 处理,你可以使用 PartialOrd::partial_cmp
。例如,如果您希望传播 NaN,请折叠:
use std::f64;
use std::cmp::Ordering;
fn main() {
let x = [2.0, 1.0, -10.0, 5.0, f64::NAN];
let min = x.iter().fold(f64::INFINITY, |a, &b| {
match PartialOrd::partial_cmp(&a, &b) {
None => f64::NAN,
Some(Ordering::Less) => a,
Some(_) => b,
}
});
println!("{}", min);
}
大概是这样的?
fn main() {
use std::cmp::Ordering;
let mut x = [2.0, 1.0, -10.0, 5.0];
x.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
println!("min in x: {:?}", x);
}
我遇到的一件事是 sort_by
就地改变向量,所以你不能直接在链中使用它。
如果您知道您的数据不包含 NaN,则通过展开比较来断言这一事实:
fn example(x: &[f64]) -> Option<f64> {
x.iter()
.cloned()
.min_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
}
如果您的数据可能有 NaN,您需要专门处理这种情况。一种解决方案是说 all 16,777,214 NaN values 彼此相等,并且总是大于或小于其他数字:
use std::cmp::Ordering;
fn example(x: &[f64]) -> Option<f64> {
x.iter()
.cloned()
.min_by(|a, b| {
// all NaNs are greater than regular numbers
match (a.is_nan(), b.is_nan()) {
(true, true) => Ordering::Equal,
(true, false) => Ordering::Greater,
(false, true) => Ordering::Less,
_ => a.partial_cmp(b).unwrap(),
}
})
}
有许多可用的 crate 可用于为您提供代码所需的任何语义。
您应该不使用partial_cmp(b).unwrap_or(Ordering::Equal)
,因为当存在 NaN 时它会提供不稳定的结果,但它会导致 reader 认为它们已被处理:
use std::cmp::Ordering;
use std::f64;
fn example(x: &[f64]) -> Option<f64> {
x.iter()
.cloned()
.min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
}
fn main() {
println!("{:?}", example(&[f64::NAN, 1.0]));
println!("{:?}", example(&[1.0, f64::NAN]));
}
Some(NaN)
Some(1.0)
A built-in total-ordering 名为 .total_cmp()
is available on nighty, and should be on stable within a couple months, baring any surprising issues. (The vote to stablize the feature recently passed 的浮点数比较方法。)这实现了 IEEE 754 中定义的总排序,每个可能的 f64 位值都是明确排序,包括正零和负零,以及所有可能的 NaN。请注意,有些 NaN 排序在 Infinity 之上,有些 NaN 排序在 -Infinity 以下,因此在存在 NaN 的情况下“最大值”可能会造成混淆,但它会保持一致。
Floats 仍然不会实现 Ord
,因此它们不会直接排序,但样板已被缩减为一行,没有任何外部导入或恐慌的机会:
#![feature(total_cmp)]
fn main() {
let mut a: Vec<f64> = vec![2.0, 2.5, -0.5, 1.0, 1.5];
let maximum = *a.iter().max_by_key(f64::total_cmp).unwrap();
println!("The maximum value was {maximum}.");
a.sort_by(f64::total_cmp);
}