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

标签 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()
}

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

最佳答案

如果我想实现 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 绑定(bind),因为未为未签名的特征实现此绑定(bind);所以如果我们需要Neg,我们必须添加它;但这很容易。

至于关于常量的问题,从 向上工作确实很疯狂。这正是 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 个边界;这是一次性的
  • 如果您需要多个处理数字的通用函数,那么创建这个特征和函数的小成本在事物的宏伟计划中可以忽略不计

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

关于rust - 在 Rust 中实现通用计算算法的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35634948/

相关文章:

rust - fold 对它接受的闭包很挑剔

rust - 为什么这个右值没有提升为引用中指定的左值?

parallel-processing - 是否可以将 Rayon 和 Faster 结合起来?

rust - 在实现特性时定义预期生命周期

rust - Rust是否会缩小生命周期以满足对其定义的约束?

Rust "expected type"错误打印完全相同的不匹配类型

rust - 如何抑制测试使用的功能的 "function is never used"警告?

inheritance - 在 Rust 1.3 中继承结构的最佳方式是什么?

rust - 如何使用 std::num::sqrt 计算数字的平方根?

tcp - 对 Read trait 如何为 TcpStreams 工作的误解