在Rust中实现通用计算算法的正确方法是什么?

时间:2016-02-25 18:07:11

标签: rust

在Rust中实现通用计算算法非常麻烦。感觉就像我正在重新发明所有不在算法中的东西,而是在教堂数字的密码域中。

例如,这里是在Rust 1.7中运行的factorial的实现:

#![feature(zero_one)]

use std::num::{One, Zero};
use std::ops::{Sub, Mul};
use std::cmp::Eq;

fn fact<T>(n: T) -> T
    where T: Clone + Eq + Zero + One + Mul<T, Output = T> + Sub<T, Output = T>
{
    if n == T::zero() {
        T::one()
    } else {
        fact(n.clone() - T::one()) * n
    }
}

fn main() {
    println!("{}", fact(10));
}

有没有正确的方法呢?有没有讨论呢?

可能factorial不是很好的例子,让我们试试is_even

fn is_even<T>(x: T) -> bool
    where T: std::ops::Rem<Output = T> + std::ops::Add<T, Output=T> + std::num::One + std::num::Zero + std::cmp::PartialEq
{
    let two = T::one() + T::one();
    (x % two) == T::zero()
}

如果你想要一个two的东西,你必须重新实现两个。

1 个答案:

答案 0 :(得分:3)

如果我想实施is_even,我显然应该首先实施更为通用的is_divisible

#![feature(zero_one)]

use std::cmp;
use std::num;
use std::ops;

fn is_divisible<T>(x: T, by: T) -> bool
    where T: ops::Rem<Output = T> + num::Zero + cmp::PartialEq
{
    (x % by) == T::zero()
}

这似乎很容易。

然而,is_even有更多限制,这有点长,所以让我们关注DRY:

trait Arithmetic:
    From<u8> +
    cmp::PartialEq + cmp::Eq + cmp::PartialOrd + cmp::Ord +
    ops::Add<Self, Output = Self> + ops::Sub<Self, Output = Self> +
    ops::Mul<Self, Output = Self> + ops::Div<Self, Output = Self> + ops::Rem<Self, Output = Self> {}

impl<T> Arithmetic for T
    where T: From<u8> +
             cmp::PartialEq + cmp::Eq + cmp::PartialOrd + cmp::Ord +
             ops::Add<T, Output = T> + ops::Sub<T, Output = T> +
             ops::Mul<T, Output = T> + ops::Div<T, Output = T> + ops::Rem<T, Output = T>
 {}

好的,这个特性应该覆盖我们。请注意,它缺少ops::Neg限制,因为此绑定未针对未签名的特征实现;所以,如果我们需要Neg,我们必须添加它;但这很容易。

至于关于常数的问题,确实从zero向上工作是疯狂的。这就是ZeroOne特征仍然不稳定的原因。

一般转换特征为convert::Fromconvert::Into,这就是人们可以使用的。

因此,让我们重新制定is_divisible,最后实施is_even

fn is_divisible<T>(x: T, by: T) -> bool
    where T: Arithmetic
{
    (x % by) == 0.into()
}

fn is_even<T>(x: T) -> bool
    where T: Arithmetic
{
    is_divisible(x, 2.into())
}

实际上,这两个功能似乎都非常清晰,同时仍然是通用的。

Full code here

现在,我们可能会争辩说,创建这个Arithmetic特征是一种漫长的方式来到is_even。它是。但是:

  • 如果你只需要is_even,显然你只需要6个界限就不在乎;它是一次性的
  • 如果你需要多个处理数字的泛型函数,那么在宏观方案中创建这个特征和函数的小成本可以忽略不计

简而言之,它有效。它真的不那么繁重。