2014-11-07 13 views
22

Ho appena iniziato l'immersione in Rust e voglio fare alcune funzioni matematiche di base generiche. Ho la is_prime seguente funzione:Come posso creare una funzione is_prime generica su vari tipi di interi?

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 
} 

Cosa sarebbe necessario per me essere in grado di passare isize, i64, usize, ecc come argomenti? Ho letto lo Rust guide sulla home page ma non sono sicuro di come applicare le idee dei tratti al mio obiettivo qui.

risposta

22

I tipi di numeri generici possono essere abbastanza fastidiosi con cui lavorare, ma una volta che si ottiene il blocco di essi non tendono ad essere troppo male, anche se un po 'più prolisso. Gli elementi costitutivi standard di tali metodi sono i tratti in the num crate di crates.io, in particolare Num, Zero e One, nonché la libreria standard std::cmp::PartialOrd.

I valori letterali numerici non possono essere generici su alcun tipo numerico; devono essere fatti con una chiamata al metodo dei tratti; Zero::zero() e One::one() sono sufficienti per la maggior parte degli scopi: qui i numeri che vogliamo sono 0, 1, 2, 3, 5 e 6, che sono eminentemente realizzabili con questi blocchi predefiniti. Puoi anche creare il tuo tratto con metodi statici che producono questi valori e implementarlo per tutti i tipi numerici che ti piacciono, ma farlo con ciò che è garantito da Num è un'idea migliore.

La procedura di base è quello di specificare i parametri di tipo generico in quanto fondata su Num (e PartialOrd se si scrive le disuguaglianze sui valori di quel tipo, come ad esempio i * i <= n), e sostituire le costanti numeriche con quelli costruiti da zero e uno, come dimostra la mezza dozzina di dichiarazioni let all'inizio del metodo qui sotto. Normalmente sarà sufficiente.

Ecco cosa si finisce con questo particolare metodo:

// 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

Invece di usare il Num' tratto 'come un vincolo, è anche possibile utilizzare i tratti fondamentali che sono effettivamente necessari:' N: PartialEq + PartialOrd + Aggiungi + Sub + Mul + Rem + Uno + zero'. 'Num' è semplicemente una comoda scorciatoia. –

15

Per aggiungere alla risposta di Chris Morgan, è possibile utilizzare num::NumCast::from per lanciare un tipo di numero generica in cui utilizzando Zero e One sarebbe inappropriato. Nel tuo caso:

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(); 
Problemi correlati