【问题标题】:How do I get the minimum or maximum value of an iterator containing floating point numbers?如何获得包含浮点数的迭代器的最小值或最大值?
【发布时间】:2015-04-11 08:50:31
【问题描述】:

我了解why the floats don't have an implementation for Ord,但是当我想偷懒并使用迭代器时,这对我没有特别的帮助。

是否有解决方法或简单的方法来获取包含浮点数的迭代器的最小值/min/min_by?

我知道可以排序(这很慢)或将其包装在另一种类型中并实现所需的交易(这很冗长),但我希望有一些更优雅的东西。

【问题讨论】:

    标签: floating-point iterator rust


    【解决方案1】:

    浮点数有它们自己的 minmax 方法,可以一致地处理 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);
    }
    

    【讨论】:

    • std::cmp::partial_min 不再存在。
    【解决方案2】:

    如果您知道您的数据不包含 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 时它会提供不稳定的结果,但它会让读者认为它们已被处理:

    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.partial_cmp(b).unwrap_or_else(|| a.is_nan().cmp(&amp;b.is_nan())),更短,但可能不太好阅读。
    【解决方案3】:

    大概是这样的?

    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 会在适当的位置改变向量,因此您不能直接在链中使用它。

    【讨论】:

      猜你喜欢
      • 2019-07-31
      • 2012-04-10
      • 2020-11-04
      • 1970-01-01
      • 1970-01-01
      • 2012-09-09
      • 2014-10-23
      相关资源
      最近更新 更多