2014-11-07 7 views
22

Ich habe gerade den Tauchgang nach Rust gemacht und möchte einige grundlegende mathematische Funktionen machen, die generisch sind. Ich habe folgende is_prime Funktion:Wie kann ich eine is_prime-Funktion erstellen, die generisch für verschiedene Integer-Typen ist?

fn is_prime(n: i64) -> bool { 
    if n == 2 || n == 3 { 
     return true; 
    } else if n % 2 == 0 || n % 3 == 0 { 
     return false; 
    } 

    let mut i = 5i64; 
    let mut w = 2i64; 
    while i*i <= n { 
     if n % i == 0 { 
      return false; 
     } 
     i += w; 
     w = 6 - w; 
    } 
    true 
} 

Was würde es für mich nehmen zu können isize passieren, i64, usize usw. als Argumente? Ich habe die Rust guide auf der Homepage gelesen, aber ich bin mir nicht sicher, wie ich hier die Ideen von Merkmalen auf mein Ziel anwenden kann.

Antwort

22

Generische Zahl-Typen können ziemlich lästig sein, aber sobald man den Dreh raus hat, neigen sie nicht dazu, zu schlecht zu sein, obwohl sie ein wenig ausführlicher sind. Die Standardbausteine ​​für solche Verfahren sind die Merkmale in the num crate von crates.io, insbesondere Num, Zero und One, sowie die Standardbibliothek std::cmp::PartialOrd.

Numerische Literale dürfen nicht generisch über einen numerischen Typ sein; sie müssen mit einem Trait-Methodenaufruf gemacht werden; Zero::zero() und One::one() werden für die meisten Zwecke ausreichen - hier sind die Zahlen, die wir wollen, 0, 1, 2, 3, 5 und 6, die mit diesen Bausteinen hervorragend erreichbar sind. Sie könnten auch Ihr eigenes Merkmal mit statischen Methoden erstellen, die diese Werte erzeugen, und es für beliebige numerische Typen implementieren, die Sie mögen, aber es ist eine bessere Idee, es mit genau dem zu tun, was von Num garantiert wird.

Das grundlegende Verfahren ist Ihr generische Typparameter angeben, wie auf Num basiert (und PartialOrd wenn Sie Ungleichheiten auf Werte dieses Typs zu schreiben, wie i * i <= n), und ersetzen Sie alle Zahlenliterale mit aufgebaut diejenigen aus Null und Eins, wie das halbe Dutzend let Aussagen am Anfang der Methode unten zeigt. Das wird normalerweise genug sein.

Hier ist, was Sie mit diesem speziellen Verfahren am Ende:

// You’ll also need the appropriate dependencies.num addition to Cargo.toml 
extern crate num; 

use num::Num; 

fn is_prime<N: Num + PartialOrd + Copy>(n: N) -> bool { 
    let _0 = N::zero(); 
    let _1 = N::one(); 
    let _2 = _1 + _1; 
    let _3 = _2 + _1; 
    let _5 = _2 + _3; 
    let _6 = _3 + _3; 
    if n == _2 || n == _3 { 
     return true; 
    } else if n % _2 == _0 || n % _3 == _0 { 
     return false; 
    } 

    let mut i = _5; 
    let mut w = _2; 
    while i * i <= n { 
     if n % i == _0 { 
      return false; 
     } 
     i = i + w; 
     w = _6 - w; 
    } 
    true 
} 
+5

Statt die 'Num' Eigenschaft als Einschränkung der Verwendung, dann ist es auch möglich, die Grundzüge zu verwenden, die tatsächlich benötigt werden:' N: PartialEq + PartialOrd + hinzufügen + Sub + Mul + Rem + Eins + Null'. 'Num' ist einfach eine bequeme Abkürzung. –

15

Chris Morgan Antwort hinzuzufügen, können Sie num::NumCast::from verwenden, um eine generische Nummer Art zu werfen, wo Zero und One Verwendung wäre unangemessen. In Ihrem Fall:

use num::{Num, NumCast}; 

fn is_prime<N: Num + Ord + NumCast + Copy>(n: N) -> bool { 
    let _0: N = NumCast::from(0usize).unwrap(); 
    let _1: N = NumCast::from(1usize).unwrap(); 
    let _2: N = NumCast::from(2usize).unwrap(); 
    let _3: N = NumCast::from(3usize).unwrap(); 
    let _4: N = NumCast::from(4usize).unwrap(); 
    let _5: N = NumCast::from(5usize).unwrap(); 
    let _6: N = NumCast::from(6usize).unwrap(); 
Verwandte Themen