Immutable Dictionary

Is there a way to make the dictionary persistent?

I have a function that reads a file for parameters (and ignores comments) and saves it in a dict file:

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 

This works great. Since I have many parameters that I will use in different places, my idea was to make this dictate global. And, as we all know, global variables are not so large, so I wanted to make sure that dict and its members are immutable.

Is this a good approach? How can I do it? Should I do this?


Bonus response material :)

Is my code at all? (This is the first thing I did with julia, and based on c / C ++ and python, I have a tendency to do things differently.) Do I need to check if the file is really open? Am I reading the Julia file? I could also readall and then use eachmatch . I don't see the “right way to do this” (e.g. in python).

+7
dictionary julia-lang
source share
4 answers

Why not use an ImmutableDict? It is defined in the database, but not exported. You use it as follows:

 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 

You can define a constructor that takes an array of pairs (or keys and values) and uses this algorithm to determine the whole dict (which is the only way to do this, see note below).


Just a added note, the actual internal representation is that each dictionary contains only one key-value pair and a dictionary. The get method simply scans the dictionaries to see if it has the correct value. The reason for this is that arrays are mutable: if you made a naive construction of an immutable type with a mutable field, the field is still changed and thus, while id["key1"]=2 will not work, id.keys[1]=2 will be. They get around this by not using a mutable type to store values ​​(while preserving only single values), and then also preserving an immutable dict. If you want to do this work directly on arrays, you can use something like ImmutableArrays.jl , but I don't think you are a performance advantage, because you still have to iterate over the array when checking the key ...

+5
source share

REVISION

Thanks to Chris Rakaukas for pointing out the error in my previous answer. I will leave this below as an illustration of what is not working. But Chris is right, the const declaration doesn’t actually improve performance when feeding a dictionary into a function. So see Chris's answer for the best solution to this problem:

 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) 

Old answer

If you want your dictionary to be constant, you can use:

 const MyDict = getparameters( .. ) 

Refresh . Remember that in the Julia database, unlike some other languages, this does not mean that you cannot redefine constants, instead you just get a warning at the same time.

 julia> const a = 2 2 julia> a = 3 WARNING: redefining constant a 3 julia> a 3 

It is odd that you do not receive a warning about constant overriding when adding a new key-shaft pair to the dictionary. But you still see performance improvements from declaring it as constants:

 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) 
+4
source share

Firstly, I am new to Julia (I only use / study it in two weeks). Therefore, do not make sure that I am going to say if this is not confirmed by others.

Dict dictionary data structure defined here

 julia/base/dict.jl 

This file also has a data structure called ImmutableDict . However, since const variables are not actually const, why will immutable dictionaries be immutable?

The comment says:

ImmutableDict is a dictionary implemented as an immutable linked list that is optimal for small dictionaries that are built on many separate inserts. Note that it is impossible to delete a value, although it can be partially redefined and hidden by adding a new value with the same key

So let us name what you want to define as a UnmodifiableDict dictionary to avoid confusion. Such an object would probably have

  • similar Dict data structure.
  • a constructor that takes a Dict as input to populate its data structure.
  • specialization (new dispatch?) of the setindex! method setindex! , which is called by the operator [] = to prohibit modification of the data structure. This should be the case for all other functions that end with ! and therefore modify the data.

As I understand it, it is possible to have subtypes of abstract types. Therefore, you cannot make UnmodifiableDict a subtype of Dict and only redefine functions like setindex!

Unfortunately, this is a necessary limitation for the presence of runtime types, not compilation time types. You cannot have such good performance without any restrictions.

Bottom line:

The only solution I see is to copy code like Dict and its functions, replace Dict with UnmodifiableDict everywhere and change functions that end in ! to throw an exception if called.

You can also look at these topics.

+4
source share

To add to existing answers, if you like immutability and want to get executable (but still constant) operations that change and expand the dictionary, see FunctionalCollections.jl PersistentHashMap .

If you want to maximize performance and make the most of immutability, and you do not plan to do any operations on the dictionary, consider implementing a perfect hash function . In fact, if your dictionary is a compile-time constant, they can even be pre-calculated (using metaprogramming) and pre-compiled.

+3
source share

All Articles