rust 在泛型中,如果是整数,是否可以加1;如果是浮点,是否可以加0.1?

5m1hhzi4  于 2022-11-24  发布在  其他
关注(0)|答案(3)|浏览(167)

我有一个泛型,其中Numnum crate中定义:

//finds `x` where `f(x) == Equal` for `x ∈ [left, right)`
fn binary_search<T: Debug + Copy + PartialOrd + Num, F: FnMut(T) -> Ordering>(
    mut left: T,
    mut right: T, //exclusive
    mut f: F,
) -> Result<T, T> {
    while (left < right) {
        let mid = left + (right - left) / (T::one() + T::one());
        match f(mid) {
            Less => left = mid + T::one(),
            Greater => right = mid,
            Equal => return Ok(mid),
        }
    }
    Err(left)
}

这种二分查找对整型正确,但对浮点型(如f64)无效。

Less => left = mid + T::one(),

T::one()应该改为1e-6(浮点型)和1(整型)。这可能吗?
这是Rust Playground,其中包含一些可用的自动化测试。我希望在不破坏binary_search_01()的情况下使binary_search_02()通过。

ctehm74n

ctehm74n1#

LessGreater臂并不对称,这就是为什么你必须调整其中一个的中点,而不是另一个。你可以通过使两个端点都包括在内来修复它,这样就不需要轻推中点了。

pub fn binary_search<
    T: Debug + Copy + PartialOrd + Num,
    F: FnMut(T) -> Ordering,
>(
    mut left: T,  // inclusive
    mut right: T, // inclusive
    mut f: F,
) -> Result<T, T> {
    loop {
        let mid = left + (right - left) / (T::one() + T::one());
        match f(mid) {
            Less => {
                if left == mid {
                    break;
                }
                left = mid;
            }
            Greater => {
                if right == mid {
                    break;
                }
                right = mid;
            }
            Equal => return Ok(mid),
        }
    }
    Err(right)
}

Playground
请注意,我将最终返回值更改为Err(right)以使测试通过。我不确定这是否正确?如果不正确,您可以尝试使用它。

mmvthczy

mmvthczy2#

您可以定义自己的trait,并为整数和浮点类型实现它:

trait Delta {
    fn delta() -> Self;
}

impl Delta for i32 {
    fn delta() -> Self { 1 }
}

impl Delta for f32 {
    fn delta() -> Self { 1e-6 }
}

fn main() {
    println!("i32: {}", i32::delta());
    println!("f32: {}", f32::delta());
}

如果您不想为所有类型手动实现Delta,可以使用宏来实现:

macro_rules! impl_delta {
    ($v:expr, $($t:ty),*) => {
        $(
        impl Delta for $t {
            fn delta() -> Self { $v }
        }
        )*
    }
}

impl_delta!(1, i8, i16, i32, i64);
impl_delta!(1e-6, f32, f64);

Playground

svdrlsy4

svdrlsy43#

您可以在定义了某些转换方法的位置使用num::FromPrimitive trait

pub trait FromPrimitive {
    pub fn from_i64(n: i64) -> Option<Self>;
    pub fn from_u64(n: u64) -> Option<Self>;
    /* snip */
    pub fn from_f32(n: f32) -> Option<Self> { ... }
    pub fn from_f64(n: f64) -> Option<Self> { ... }
}

因此,将delta定义为

let delta = {
    let delta = T::from_f64(1e-9).unwrap();
    if (delta == T::zero()) { //if integer types
        T::one()
    } else { //if float types
        delta
    }
};

并添加它而不是T::one()

Less => left = mid + delta,

最后的代码是这样的,它使所有的测试都通过:

fn binary_search<
    T: fmt::Debug + Copy + cmp::PartialOrd + Num + FromPrimitive,
    F: FnMut(T) -> Ordering,
>(
    mut left: T,
    mut right: T, //exclusive
    mut f: F,
) -> Result<T, T> {
    let delta = {
        let delta = T::from_f64(1e-9).unwrap();
        if (delta == T::zero()) {
            T::one()
        } else {
            delta
        }
    };
    while (left < right) {
        let mid = left + (right - left) / (T::one() + T::one());
        match f(mid) {
            Less => left = mid + delta,
            Greater => right = mid,
            Equal => return Ok(mid),
        }
    }
    Err(left)
}

相关问题