如何使用num crate在泛型代码中表示除零和一之外的整数?

时间:2017-02-05 06:51:22

标签: rust

Rust中的num包提供了一种通过T::zero()T::one()表示零和一的方法。有没有办法表示其他整数,比如两个,三个等?

考虑以下(人为)示例:

extern crate num;

trait IsTwo {
    fn is_two(self) -> bool;
}

impl<T: num::Integer> IsTwo for T {
    fn is_two(self) -> bool {
        self == (T::one() + T::one())
    }
}

是否有更好的方式将T::one() + T::one()表示为2

3 个答案:

答案 0 :(得分:1)

在通用代码中表示任意整数的一种方法是使用num::NumCast特征:

impl<T: num::Integer + num::NumCast> IsTwo for T {
    fn is_two(self) -> bool {
        self == T::from(2).unwrap()
    }
}

相关方法是使用num::FromPrimitive特征:

impl<T: num::Integer + num::FromPrimitive> IsTwo for T {
    fn is_two(self) -> bool {
        self == T::from_i32(2).unwrap()
    }
}

相关问题和答案:[12]。

答案 1 :(得分:1)

你可以写一个函数:

Sub CommandButton1_Click()
Dim day_a, day_b As Date, point As String, east_a, north_a, height_a, height_b, east_b, north_b As Double
Dim i1, i2, i3, i4, i5, i6, i7 As Variant

    i1 = 1: i2 = 1

    Worksheets("INPUT").Cells(i1, i2).Select

    Do While Not IsEmpty(ActiveCell)
        Worksheets("INPUT").Cells(i1, i2).Activate
        If ActiveCell.Value = "Id" Then
            i3 = Split(ActiveCell.Address(columnAbsolute:=True, ReferenceStyle:=xlR1C1), "C")(1)
            MsgBox (cellidrow)
        ElseIf ActiveCell.Value = "Nord" Then
            i4 = Split(ActiveCell.Address(columnAbsolute:=True, ReferenceStyle:=xlR1C1), "C")(1)
        ElseIf ActiveCell.Value = "Øst" Then
            i5 = Split(ActiveCell.Address(columnAbsolute:=True, ReferenceStyle:=xlR1C1), "C")(1)
        ElseIf ActiveCell.Value = "S_OBJID" Then
            i6 = Split(ActiveCell.Address(columnAbsolute:=True, ReferenceStyle:=xlR1C1), "C")(1)
        ElseIf ActiveCell.Value = "DATO" Then
            i7 = Split(ActiveCell.Address(columnAbsolute:=True, ReferenceStyle:=xlR1C1), "C")(1)
        Else
        End If

        i2 = i2 + 1
    Loop

    MsgBox (i3 & i4 & i5 & i6 & i7)

    Sheets("INPUT").Cells(i5, i3).Select
    MsgBox (ActiveCell.Value)

End Sub

我之所以选择这种形式是因为它很容易被制作成一个宏,可以重复用于任何一组值:

fn two<T>() -> T 
    where T: num::Integer,
{
    let mut v = T::zero();
    for _ in 0..2 {
        v = v + T::one();
    }
    v
}

我现在听到了担忧......“但这是一个循环而且效率低下!”这就是优化编译器的用途。在发布模式下编译时,这是num_constant!(two, 2); num_constant!(forty_two, 42); 的LLVM IR:

two

这是对的 - 它已被优化为值; Function Attrs: noinline readnone uwtable define internal fastcc i32 @_ZN10playground3two17hbef99995c3606e93E() unnamed_addr #3 personality i32 (i32, i32, i64, %"unwind::libunwind::_Unwind_Exception"*, %"unwind::libunwind::_Unwind_Context"*)* @rust_eh_personality { bb3: br label %bb8 bb8: ; preds = %bb3 ret i32 2 } 。没有循环。

答案 2 :(得分:1)

从0和1伪造任何数字相对简单:

  • 你需要创建2,这几乎不困难
  • 然后继续将您的号码转换为基数2,这需要O(log2(N))操作

算法很简单:

fn convert<T: Integer>(n: usize) -> T {
    let two = T::one() + T::one();

    let mut n = n;
    let mut acc = T::one();
    let mut result = T::zero();

    while n > 0 {
        if n % 2 != 0 {
            result += acc;
        }
        acc *= two;
        n /= 2;
    }

    result
}

在Debug(O(log2(N))迭代)和Release(编译器完全优化)中都会很有效。

对于那些希望在行动中看到它的人here on the playground,我们可以看到convert::<i32>(12345)已按预期优化为12345

作为对读者的练习,实现convert的通用版本,其中包含任何Integer参数,毕竟n上的操作不多。