2016-07-15 2 views
7

Есть ли способ заставить словарь постоянным?Неизменяемый словарь

У меня есть функция, которая считывает файл параметров (и игнорирует комментарии) и сохраняет его в Словаре:

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 

Это работает просто отлично. Поскольку у меня есть много параметров, которые я буду использовать в разных местах, моя идея заключалась в том, чтобы этот дикт был глобальным. И, как мы все знаем, глобальные переменные не так велики, поэтому я хотел убедиться, что dict и его члены неизменяемы.

Это хороший подход? Как мне это сделать? Должен ли я это делать?


Bonus подотчетны материал :)

Является ли мой код даже нормально? (это первое, что я сделал с julia, и, исходя из c/C++ и python, у меня есть склонности делать что-то по-другому.) Нужно ли мне проверять, действительно ли файл открыт? Я читаю файл «джулия»? Я мог бы также readall, а затем использовать eachmatch. Я не вижу «правильный способ сделать это» (например, в python).

+1

Как правило, лучше использовать 'open (filename) do f ... end' синтаксис (см. Http://docs.julialang.org/en/release-0.4/manual/functions/#do-block- syntax-for-function-arguments), чем явно закрывать файл. Во-первых, использование блока do очистит файл, даже если возникает исключение. –

ответ

5

Почему бы не использовать ImmutableDict? Он определен в базе, но не экспортируется. Вы можете использовать один следующим образом:

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 

Вы можете определить конструктор, который принимает в виде массива пар (или ключей и значений) и использует этот алгоритм для определения всей Dict (это единственный способ сделать так, , см. примечание внизу).


Просто добавленное примечание, фактическое внутреннее представление состоит в том, что каждый словарь содержит только одну пару ключ-значение и словарь. Метод get просто просматривает словари, проверяя, имеет ли он правильное значение.Причина этого заключается в том, что массивы изменяемы: если вы сделали наивную конструкцию неизменяемого типа с изменяемым полем, поле все еще изменено и, таким образом, в то время как id["key1"]=2 не будет работать, id.keys[1]=2. Они обойдя это, не используя изменяемый тип для хранения значений (при этом сохраняя только одиночные значения), а затем также сохраняя неизменяемый dict. Если вы хотите сделать эту работу непосредственно на массивах, вы можете использовать что-то вроде ImmutableArrays.jl, но я не думаю, что вы получите преимущество в производительности, потому что вам все равно придется перебирать массив при проверке ключа ...

+0

В 0.4.6 я получаю 'ImmutableDict not defined'! –

+0

Base.ImmutableDict? –

+0

это с 'Base.ImmutableDict (" key1 "=> 1)' –

4

ПЕРЕСМОТР

Благодаря Крис Rackauckas за указание на ошибку в моем предыдущем ответе. Я оставлю это ниже в качестве иллюстрации того, что не работает. Но, Крис прав, декларация const на самом деле не улучшает производительность при подаче словаря в функцию. Таким образом, видим ответ Криса за лучшее разрешение к этому вопросу:

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) 

Старый Response

Если вы хотите, чтобы ваш словарь быть постоянным, вы можете использовать:

const MyDict = getparameters(..) 

Обновление Имейте в виду, что в базе Julia, в отличие от некоторых других языков, это не значит, что вы не не можете переопределяют константы, вместо этого просто вы получаете предупреждение при этом.

julia> const a = 2 
2 

julia> a = 3 
WARNING: redefining constant a 
3 

julia> a 
3 

Странно, что вы не получаете постоянное предупреждение переопределения при добавлении нового ключа VAL пары в словарь. Но, вы все еще видите прирост производительности от объявления его в качестве константы:

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

, но это не так. Я все еще могу добавить новые и сменить пары 'MyDict' с' MyDict [key] = value', что я и хотел запретить. – hr0m

+0

@ hr0m Хорошая точка, но вы все равно получаете повышение производительности от объявления константы. Я соответствующим образом обновил свой ответ. –

+2

Это повышение производительности не является реальным. Это связано с тем, что вы используете его в глобальной области действия, но его статически типизировали из-за константы. Поместите его в функцию повторного запуска тестов. Const отличается от изменчивости, потому что то, что он действительно делает, говорит компилятору, что вы не будете изменять тип, а не значения. По этой причине это не очень хороший ответ, поскольку истинная неизменяемая структура не сможет меняться и может получить лучшую производительность, основанную на этом (что не связано с неправильным выбором) –

4

Во-первых, я новичок в Джулии (я использую/изучать его, так как только две недели). Поэтому не делайте уверенности в том, что я собираюсь сказать, если это не подтверждается другими.

Словарь Структура Dict данных определяется здесь

julia/base/dict.jl 

Существует также структура данных, называемая ImmutableDict в этом файле. Однако, поскольку переменные const на самом деле не являются константами, почему неизменяемые словари будут неизменными?

Комментарий гласит:

ImmutableDict является словарь реализован в виде неизменного связанного списка, , которая является оптимальной для небольших словарей, построенных в течение многих отдельных вставок Обратите внимание, что это не возможно, чтобы удалить значение , хотя она может быть частично отменено и скрытые , вставляя новое значение с тем же ключом

так давайте называть то, что вы хотите, чтобы определить, как словарь UnmodifiableDict, чтобы избежать сотрудничества nfusion. Такой объект, вероятно, будет иметь

  • подобная структура данных как Dict.
  • конструктор, который принимает Dict в качестве входных данных для заполнения своей структуры данных.
  • специализация (новая отправка?) Метода setindex!, который вызывается оператором [] = , чтобы запретить модификацию структуры данных. Это должно быть в случае всех других функций, которые заканчиваются ! и, следовательно, изменяют данные.

Насколько я понял, возможно иметь подтипы абстрактных типов. Поэтому вы не можете сделать UnmodifiableDict как подтип Dict и только переопределять функции, такие как setindex!

К сожалению, это необходимо ограничение для имеющих типов времени выполнения, а не во время компиляции типов. Вы не можете иметь такую ​​хорошую производительность без каких-либо ограничений.

Итог:

Единственное решение, которое я вижу, чтобы скопировать вставьте код типа Dict и его функции, заменить Dict на UnmodifiableDict везде и модифицировать функции, которые заканчиваются ! поднять исключение, если называется.

Вы также можете просмотреть эти темы.

3

Чтобы добавить к существующим ответам, если вы хотите неизменность и хотите получить исполняемые (но все же постоянные) операции, которые меняют и расширяют словарь, ознакомьтесь с номером FunctionalCollections.jl's PersistentHashMap.

Если вы хотите максимизировать производительность и максимально использовать непреложность, и вы не планируете делать какие-либо операции над этим словарем, подумайте над внедрением словаря perfect hash function. Фактически, если ваш словарь является константой времени компиляции, их можно даже заранее вычислить (используя метапрограммирование) и предварительно скомпилировать.