如何让函数可以处理整数或浮点数?

10

找到了计算平均值的函数,并开始尝试使用它。下面的代码片段可以运行,但如果输入的数据从浮点数变为整数,则会出现错误。如何使其既可以处理浮点数又能处理整数?

use std::borrow::Borrow;

fn mean(arr: &mut [f64]) -> f64 {
    let mut i = 0.0;
    let mut mean = 0.0;
    for num in arr {
        i += 1.0;
        mean += (num.borrow() - mean) / i;
    }
    mean
}

fn main() {
    let val = mean(&mut vec![4.0, 5.0, 3.0, 2.0]);
    println!("The mean is {}", val);
}

1
当你说“使用整数”时,返回类型应该是整数还是浮点数? - Matthieu M.
根据选择的算法,我认为 OP 想要始终返回 f64,但也接受整数输入。 - @MatthieuM - user4815162342
另外还可以看看num库,我认为这个库没有被充分利用。它定义了zeroone特性,可以用于像sumproduct这样的函数。 - user25064
1个回答

18
问题中的代码无法编译,因为 f64 没有 borrow() 方法。此外,它接受的切片不需要是可变的,因为我们没有改变它。这是一个经过修改可以编译和工作的版本:
fn mean(arr: &[f64]) -> f64 {
    let mut i = 0.0;
    let mut mean = 0.0;
    for &num in arr {
        i += 1.0;
        mean += (num - mean) / i;
    }
    mean
}

我们在循环arr时指定&num,以便num的类型为f64而不是对f64的引用。这个片段可以使用两者,但省略它将破坏通用版本。
为了使相同的函数接受浮点数和整数,它的参数需要是通用的。理想情况下,我们希望它接受任何可转换为f64的类型,包括f32或定义了这种转换的用户定义类型。像这样:
fn mean<T>(arr: &[T]) -> f64 {
    let mut i = 0.0;
    let mut mean = 0.0;
    for &num in arr {
        i += 1.0;
        mean += (num as f64 - mean) / i;
    }
    mean
}

这段代码无法编译,因为对于任意类型的xx as f64未定义。相反,我们需要在T上定义一个trait bound,以定义将T值转换为f64的方法。这正是Into trait的目的;每个实现Into<U>的类型T都定义了一个into(self) -> U方法。将T: Into<f64>指定为trait bound,给我们返回f64into()方法。
我们还需要请求TCopy,以防止从数组中读取值“消耗”该值,即尝试将其移出数组。由于原始数字(如整数)实现了Copy,因此对我们来说这是可以的。工作代码如下:
fn mean<T: Into<f64> + Copy>(arr: &[T]) -> f64 {
    let mut i = 0.0;
    let mut mean = 0.0;
    for &num in arr {
        i += 1.0;
        mean += (num.into() - mean) / i;
    }
    mean
}

fn main() {
    let val1 = mean(&vec![4.0, 5.0, 3.0, 2.0]);
    let val2 = mean(&vec![4, 5, 3, 2]);
    println!("The means are {} and {}", val1, val2);
}

请注意,这仅适用于定义了无损转换为f64的类型。因此,它适用于u32i32(如上例所示)和更小的整数类型,但不接受例如i64u64的向量,因为它们无法无损地转换为f64

还要注意,这个问题很适合函数式编程习惯,比如enumerate()fold()。虽然超出了这个已经很长的答案的范围,但编写这样的实现是一种难以抵制的练习难以抵制


请注意:在本例中,我假设val2使用Vec<i32>,并且i32可以无损转换为f64,因此已实现Into<f64>;但我认为对于i64不起作用。 - Matthieu M.
@MatthieuM。这种行为听起来像是一个合理的默认设置,尽管可能不符合用户的期望。我现在已经明确在答案中提到了它。 - user4815162342
2
这是合理的,我只是更喜欢“全面披露”。回答得好 :) - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接