不变的字典

时间:2016-07-15 17:35:26

标签: dictionary julia

有没有办法强制字典保持不变?

我有一个函数可以读取参数文件(并忽略注释)并将其存储在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

这很好用。由于我有很多参数,我将最终在不同的地方使用,我的想法是让这个词是全局的。众所周知,全局变量并不那么好,所以我想确保dict及其成员是不可变的。

这是一个好方法吗?我该怎么做?我必须这样做吗?

Bonus answerable stuff:)

我的代码还可以吗? (这是我用julia做的第一件事,来自c / c ++和python我倾向于以不同的方式做事。)我是否需要检查文件是否实际打开?我读的文件是“julia”吗?我也可以readall然后使用eachmatch。我没有看到“正确的方法”(如在python中)。

4 个答案:

答案 0 :(得分:4)

为什么不使用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(这是唯一的方法,请参见底部的注释)。 / p>

只是一个补充说明,实际的内部表示是每个字典只包含一个键值对和一个字典。 get方法只是遍历字典,检查它是否具有正确的值。这样做的原因是因为数组是可变的:如果你使用可变字段进行了不可变类型的天真构造,那么该字段仍然是可变的,因此id["key1"]=2不起作用,id.keys[1]=2会。他们通过不使用可变类型来保存值(因此仅保存单个值)然后还持有不可变的dict来解决这个问题。如果你想直接在数组上做这个工作,你可以使用类似ImmutableArrays.jl之类的东西,但我不认为你会获得性能优势,因为你在检查时仍需要遍历数组键...

答案 1 :(得分:3)

<强> REVISION

感谢Chris Rackauckas指出我之前的回复中的错误。我将把它留在下面,作为不起作用的例证。但是,Chris是对的,当你将字典输入函数时,const声明实际上似乎并没有提高性能。 因此,请参阅Chris对此问题的最佳解决方案的回答

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)

旧回复

如果您希望字典为常量,可以使用:

const MyDict = getparameters( .. )

更新请记住,虽然在基础朱莉娅中,与其他语言不同,并不是无法重新定义常量,而是仅仅是当您收到警告时这样做。

julia> const a = 2
2

julia> a = 3
WARNING: redefining constant a
3

julia> a
3

在向字典添加新的key-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)

答案 2 :(得分:3)

首先,我是朱莉娅的新手(我已经使用/学习它仅两周了)。所以除非经过其他人的验证,否则不要对我要说的内容充满信心。

字典数据结构Dict在这里定义

julia/base/dict.jl

该文件中还有一个名为ImmutableDict的数据结构。但是,由于const变量实际上不是const,为什么不可变字典是不可变的?

评论指出:

  

ImmutableDict是一个实现为不可变链表的字典,   这对于在许多单独插入上构建的小字典是最佳的   请注意,虽然可以部分覆盖和隐藏值,但无法删除值   通过插入具有相同键的新值

因此,让我们将您想要定义的内容称为字典UnmodifiableDict以避免混淆。这样的对象可能有

  • Dict类似的数据结构。
  • 一个构造函数,它以Dict作为输入来填充其数据结构。
  • 运算符调用的方法setindex!
  • 特化(一个新的调度?)? 为了禁止修改数据结构。这应该是以!结尾并因此修改数据的所有其他函数的情况。

据我所知,只有抽象类型的子类型才有可能。因此,您无法将UnmodifiableDict作为Dict的子类型,只能重新定义setindex!

等函数

不幸的是,这是运行时类型而非编译时类型的必要限制。没有一些限制,你不可能有这么好的表现。

底线:

我看到的唯一解决方案是复制粘贴Dict类型的代码及其功能,将Dict替换为UnmodifiableDict,并修改以{{1}结尾的函数如果被调用则引发异常。

您可能还想查看这些主题。

答案 3 :(得分:2)

要添加现有答案,如果您想要不变性并希望获得改变和扩展字典的高性能(但仍然持久)操作,请查看FunctionalCollections.jl&#39; s PersistentHashMap类型。

如果您希望最大限度地提高性能并最大限度地利用不变性,并且您不打算在字典上进行任何操作,请考虑实施基于perfect hash function的字典。实际上,如果你的字典是编译时常量,那么甚至可以提前计算它们(使用元编程)并预编译。