2016-03-26 10 views
2

Ich habe eine teure Funktion wie folgt aus:Was ist die idiomatische Methode, Caching für eine Funktion zu implementieren, die keine struct-Methode ist?

pub fn get_expensive_value(n: u64): u64 { 
    let ret = 0; 
    for 0 .. n { 
     // expensive stuff 
    } 
    ret 
} 

Und es sehr häufig mit dem gleichen Argument aufgerufen wird. Es ist rein, das heißt, es wird das gleiche Ergebnis zurückgeben und kann einen Cache verwenden.

Wenn dies eine Struct-Methode wäre, würde ich ein Mitglied der Struktur hinzufügen, die als Cache fungiert, aber es ist nicht. Also meine Option, um eine statische Aufladung zu sein scheint, zu verwenden:

static mut LAST_VAL: Option<(u64, u64)> = None; 

pub fn cached_expensive(n: u64) -> u64 { 
    unsafe { 
     LAST_VAL = LAST_VAL.and_then(|(k, v)| { 
      if k == n { 
       Some((n,v)) 
      } else { 
       None 
      } 
     }).or_else(|| { 
      Some((n, get_expensive_value(n))) 
     }); 
     let (_, v) = LAST_VAL.unwrap(); 
     v 
    } 
} 

Nun, ich habe unsafe zu verwenden. Anstelle der static mut könnte ich eine RefCell in eine const setzen. Aber ich bin nicht davon überzeugt, dass es sicherer ist - es vermeidet einfach, den Block unsafe zu benutzen. Ich dachte an eine Mutex, aber ich denke nicht, dass mich das auch Threadsicherheit bringen wird.

Die Neugestaltung des Codes zur Verwendung einer Struktur für die Speicherung ist nicht wirklich eine Option.

+0

Duplizieren von [Wie erstelle ich eine globale, wandelbar Singletons?] (http://stackoverflow.com/questions/27791532/how- do-i-create-a-global-veränderbares Singleton). – Shepmaster

+0

Oder Sie können die Signatur von "cached_expensive" ändern, um den Cache als weiteren Parameter zu akzeptieren. – Shepmaster

+0

Ich glaube nicht, dass dies ein Duplikat ist. Meine Frage bezieht sich speziell auf das Caching und, während mein Ausgangspunkt ein globaler, veränderbarer Singleton ist, ist das nebensächlich und eine gute Lösung könnte erklären, wie (zum Beispiel) eine 'RefCell' oder' Mutex' dies besser machen oder komplett anbieten könnte verschiedene alternative strukturell. –

Antwort

1

Ich denke, die beste Alternative ist die Verwendung einer globalen Variable mit einem Mutex. lazy_static macht es leicht und ermöglicht die „globale“ Deklaration innerhalb der Funktion

pub fn cached_expensive(n: u64) -> u64 { 
    use std::sync::Mutex; 
    lazy_static! { 
     static ref LAST_VAL: Mutex<Option<(u64, u64)>> = Mutex::new(None); 
    } 
    let mut last = LAST_VAL.lock().unwrap(); 
    let r = last.and_then(|(k, v)| { 
     if k == n { 
      Some((n, v)) 
     } else { 
      None 
     } 
    }).or_else(|| Some((n, get_expensive_value(n)))); 
    let (_, v) = r.unwrap(); 
    *last = r; 
    v 
} 
+0

Sie stimmen also zu, dass dies ein Duplikat von [Wie erstelle ich ein globales, veränderliches Singleton?] (Http://stackoverflow.com/questions/27791532/how) -do-i-create-a-global-veränderbar-Singleton) wie in den Kommentaren vorgeschlagen? – Shepmaster

+0

Ja. Aber ich entscheide mich dafür, die Antwort zu schreiben, um zu zeigen, dass die globale Variable in der Funktion deklariert werden kann, also der Bereich lokal ist. – malbarbo

Verwandte Themen