2016-07-15 107 views
7

C'è un modo per far sì che un dizionario sia costante?Dizionario immutabile

Ho una funzione che legge un file per i parametri (e ignora commenti) e lo memorizza in un dict:

function getparameters(filename::AbstractString) 
    f = open(filename,"r") 
    dict = Dict{AbstractString, AbstractString}() 
    for ln in eachline(f) 
     m = match(r"^\s*(?P<key>\w+)\s+(?P<value>[\w+-.]+)", ln) 
     if m != nothing 
      dict[m[:key]] = m[:value] 
     end 
    end 
    close(f) 
    return dict 
end 

Questo funziona bene. Dato che ho molti parametri, che finirò per usare in posti diversi, la mia idea era di lasciare che questo dict fosse globale. E come tutti sappiamo, le variabili globali non sono così grandi, quindi volevo assicurarmi che il ditt ei suoi membri fossero immutabili.

È un buon approccio? Come lo faccio? Devo farlo?


Bonus roba risponde :)

è il mio codice ancora ok? (È la prima cosa che ho fatto con julia, e venendo da c/C++ e python ho la tendenza a fare le cose in modo diverso.) Devo controllare se il file è effettivamente aperto? La mia lettura del file "julia" è simile? Potrei anche readall e quindi utilizzare eachmatch. Non vedo il "modo giusto per farlo" (come in Python).

+1

Generalmente è meglio utilizzare la sintassi 'open (nomefile) do f ... end' (consultare http://docs.julialang.org/en/release-0.4/manual/functions/#do-block- sintassi-per-funzione-argomenti) piuttosto che chiudere il file esplicitamente. Per uno, l'uso del blocco do pulirà il file anche se si verifica un'eccezione. –

risposta

5

perché non utilizzare un ImmutableDict? È definito in base ma non esportato. Si utilizza uno come segue:

julia> id = Base.ImmutableDict("key1"=>1) 
Base.ImmutableDict{String,Int64} with 1 entry: 
    "key1" => 1 

julia> id["key1"] 
1 

julia> id["key1"] = 2 
ERROR: MethodError: no method matching setindex!(::Base.ImmutableDict{String,Int64}, ::Int64, ::String) 
in eval(::Module, ::Any) at .\boot.jl:234 
in macro expansion at .\REPL.jl:92 [inlined] 
in (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at .\event.jl:46 

julia> id2 = Base.ImmutableDict(id,"key2"=>2) 
Base.ImmutableDict{String,Int64} with 2 entries: 
    "key2" => 2 
    "key1" => 1 

julia> id.value 
1 

È possibile definire un costruttore che prende in un array di coppie (o chiavi e valori) e utilizza tale algoritmo per definire tutta dict (che è l'unico modo per farlo , vedi la nota in fondo).


Solo una nota aggiunta, la rappresentazione interna reale è che ogni dizionario contiene solo una coppia chiave-valore, e un dizionario. Il metodo get passa semplicemente attraverso i dizionari controllando se ha il giusto valore.La ragione di ciò è dovuta al fatto che gli array sono mutabili: se hai fatto una ingenua costruzione di un tipo immutabile con un campo mutabile, il campo è ancora mutabile e quindi, mentre id["key1"]=2 non funzionerebbe, lo farebbe id.keys[1]=2. Essi aggirano questo problema non utilizzando un tipo mutabile per mantenere i valori (mantenendo così solo i singoli valori) e quindi anche tenendo un dettato immutabile. Se si desidera eseguire questo lavoro direttamente sugli array, è possibile utilizzare qualcosa come ImmutableArrays.jl ma non penso che si otterrà un vantaggio in termini di prestazioni perché si dovrebbe ancora eseguire il ciclo attraverso la matrice durante il controllo di una chiave ...

+0

In 0.4.6 ottengo 'ImmutableDict non definito'! –

+0

Base.ImmutableDict? –

+0

che è con 'Base.ImmutableDict (" key1 "=> 1)' –

4

REVISIONE

Grazie a Chris Rackauckas per sottolineare l'errore nel mio precedente risposta. Lo lascerò qui sotto come illustrazione di ciò che non funziona. Ma, Chris ha ragione, la dichiarazione const in realtà non sembra migliorare le prestazioni quando si inserisce il dizionario nella funzione. Così, vedi risposta Chris' per la migliore risoluzione a questo problema:

D1 = [i => sind(i) for i = 0.0:5:3600]; 
const D2 = [i => sind(i) for i = 0.0:5:3600]; 

function test(D) 
    for jdx = 1:1000 
     # D[2] = 2 
     for idx = 0.0:5:3600 
      a = D[idx] 
     end  
    end 
end 

## Times given after an initial run to allow for compiling 
@time test(D1); # 0.017789 seconds (4 allocations: 160 bytes) 
@time test(D2); # 0.015075 seconds (4 allocations: 160 bytes) 

vecchia risposta

Se volete che il vostro dizionario per essere una costante, è possibile utilizzare:

const MyDict = getparameters(..) 

Aggiornamento Tenete presente però che nella base Julia, a differenza di altre lingue, non è che voi non è possibile ridefinisci le costanti, invece, è solo che ricevi un avvertimento quando lo fai.

julia> const a = 2 
2 

julia> a = 3 
WARNING: redefining constant a 
3 

julia> a 
3 

E 'strano che non si ottiene l'avvertimento ridefinizione costante quando l'aggiunta di una nuova coppia di chiavi-val al dizionario. Ma, si vede ancora il miglioramento delle performance rispetto dichiarandolo come una costante:

D1 = [i => sind(i) for i = 0.0:5:3600]; 
const D2 = [i => sind(i) for i = 0.0:5:3600]; 

function test1() 
    for jdx = 1:1000 
     for idx = 0.0:5:3600 
      a = D1[idx] 
     end  
    end 
end 


function test2() 
    for jdx = 1:1000 
     for idx = 0.0:5:3600 
      a = D2[idx] 
     end  
    end 
end 

## Times given after an initial run to allow for compiling 
@time test1(); # 0.049204 seconds (1.44 M allocations: 22.003 MB, 5.64% gc time) 
@time test2(); # 0.013657 seconds (4 allocations: 160 bytes) 
+0

ma non lo è. Posso ancora aggiungere nuove e cambiare coppie di 'MyDict' con' MyDict [chiave] = valore', che è esattamente quello che volevo proibire. – hr0m

+0

@ hr0m Buon punto, ma si ottiene comunque il miglioramento delle prestazioni dal dichiararlo costante. Ho aggiornato la mia risposta di conseguenza. –

+2

Questo aumento delle prestazioni non è reale. È perché lo stai eseguendo nell'ambito globale ma è tipizzato staticamente a causa di const. Mettilo in una funzione e riesegui i test. Const è diverso dalla mutabilità perché ciò che sta facendo è dire al compilatore che non cambierai il tipo, non i valori. Per questo motivo non è davvero una buona risposta in quanto una struttura immutabile non sarebbe in grado di cambiare e può ottenere prestazioni migliori basate su quello (che non è legato a scelte di scoping errate) –

4

Prima di tutto, io sono nuovo di Julia (ho usato/apprendimento è dato solo due settimane). Quindi non fidarti di quello che dirò a meno che non sia convalidato da altri.

La struttura dei dati dizionario Dict è qui definito

julia/base/dict.jl 

V'è anche una struttura di dati denominata ImmutableDict in quel file. Tuttavia, poiché le variabili const non sono effettivamente costanti, perché i dizionari immutabili potrebbero essere immutabili?

il commento afferma:

ImmutableDict è un dizionario implementato come una lista concatenata immutabile, che è ottimale per i piccoli dizionari che sono costruiti nel corso di molti singoli inserimenti Nota che non è possibile rimuovere un valore , anche se può essere parzialmente ignorato e nascosto inserendo un nuovo valore con la stessa chiave

Cerchiamo quindi di chiamare ciò che si desidera definire come un dizionario UnmodifiableDict per evitare co nFusion. Tale oggetto avrebbe probabilmente

  • una struttura di dati simile a Dict.
  • un costruttore che prende un Dict come input per riempire la sua struttura di dati.
  • specializzazione (una nuova spedizione?) Del metodo setindex! che viene chiamato dall'operatore [] = per impedire la modifica della struttura dati. Questo dovrebbe essere il caso di tutte le altre funzioni che terminano con ! e quindi modificano i dati.

Per quanto ho capito, è possibile avere solo sottotipi di tipi astratti. Pertanto non è possibile effettuare UnmodifiableDict come un sottotipo di Dict e ridefinire solo funzioni come setindex!

Purtroppo questa è una limitazione necessaria per avere i tipi di run-time e non a tempo di compilazione tipi. Non puoi avere una prestazione così buona senza alcune restrizioni.

Bottom line:

L'unica soluzione che vedo è quella di copiare incollare il codice del tipo Dict e le sue funzioni, sostituire Dict da UnmodifiableDict ovunque e modificare le funzioni che terminano con ! a sollevare un'eccezione se chiamato.

potresti anche voler dare un'occhiata a quei thread.

3

Per aggiungere alle risposte esistenti, se ti piace l'immutabilità e desideri ottenere operazioni performanti (ma ancora persistenti) che modificano ed estendono il dizionario, controlla il tipo FunctionalCollections.jl di PersistentHashMap.

Se si desidera ottimizzare le prestazioni e sfruttare al massimo l'immutabilità e non si intende eseguire alcuna operazione sul dizionario, considerare l'implementazione di un dizionario basato su perfect hash function. Infatti, se il tuo dizionario è una costante in fase di compilazione, questi possono anche essere calcolati in anticipo (usando metaprogramming) e precompilati.