2016-02-25 110 views
-1

在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的东西,你必须重新实现两项。

+2

这似乎并不像Stack Overflow可以回答的狭隘目标问题。 “我如何实现一个通用算法”是一个非常广泛的话题。正如我写的,你的代码看起来像我期望的阶乘的定义。 *对我来说,你的问题更多的是咆哮而不是一个善意的问题,但这可能只是我个人的解释。 – Shepmaster

+0

@Shepmaster如果发现'factorial',那么这个'is_even'怎么样? – Adam

+2

您可以通过使用[num crate](https://github.com/rust-num/num)中的特征来减少边界,但实现将保持不变。您如何期望is_even的通用实现看起来理想? –

回答

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更是把约束,这是有点长,所以让我们跟着干:

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个边界;这是一个一次性
  • 如果你需要NUMERICS工作的多个通用功能,然后创建这种特质和功能的小成本的事情

总之宏伟计划可以忽略不计,它的工作原理。这真的不是那么繁重。

相关问题