Julia入門 辞書(ハッシュテーブル)、Set型について

Juliaの配列以外の、データ構造について。

Dict (辞書/ハッシュテーブル)

JuliaだとAssociative Collectionというらしい。

現在Stableのver.0.3と、ver0.4でリテラルでの宣言の仕方が変わるので注意したい。

明示的に型を指定しない方法

まず0.3では以下の通り。

julia> versioninfo()
Julia Version 0.3.8
Commit 79599ad (2015-04-30 23:40 UTC)
(略)

julia> anydict = {"a" => 123, "b" => 456}
Dict{Any,Any} with 2 entries:
  "b" => 456
  "a" => 123

julia> normaldict = ["a" => 123, "b" => 456]
Dict{ASCIIString,Int64} with 2 entries:
  "b" => 456
  "a" => 123

{} (中カッコ)で宣言すると、{Any, Any}型の辞書が作られる。
[] (各カッコ)で宣言すると、内容から自動で型を推定してくれる。上の例では、おなじ中身でも後者はDict{ASCIIString,Int64}の型になっていることに注意されたし。

これが、ver.0.4だと

julia> versioninfo()
(略)

julia> anydict = {"a" => 123, "b" => 456}

WARNING: deprecated syntax "{a=>b, ...}".
Use "Dict{Any,Any}(a=>b, ...)" instead.
Dict{Any,Any} with 2 entries:
  "b" => 456
  "a" => 123

julia> normaldict = ["a" => 123, "b" => 456]

WARNING: deprecated syntax "[a=>b, ...]".
Use "Dict(a=>b, ...)" instead.
Dict{ASCIIString,Int64} with 2 entries:
  "b" => 456
  "a" => 123

julia> Dict("a" => 123, "b" => 456)
Dict{ASCIIString,Int64} with 2 entries:
  "b" => 456
  "a" => 123

ver.0.3の書き方は両方depricated警告が出る。
後者の型を自動で推理してくれる宣言の仕方は、Dict(key => value, ...) になり、前者の{Any, Any}型でのリテラルの宣言は、方法自体が廃止になった。

型を指定して宣言する方法

これも、ver0.3 / 0.4で異なる。ver.0.3では以下の通り。

julia> versioninfo()
Julia Version 0.3.8
Commit 79599ad (2015-04-30 23:40 UTC)
(略)

julia> (ASCIIString=>Int64)["abc" => 123, "def" => 456]
Dict{ASCIIString,Int64} with 2 entries:
  "abc" => 123
  "def" => 456

ver.0.4では以下のとおり。

julia> versioninfo()
Julia Version 0.4.0-dev+5114
Commit 894a31e* (2015-05-30 03:57 UTC)
(略)

julia> Dict{ASCIIString, Int64}("abc" => 123, "def" => 456)
Dict{ASCIIString,Int64} with 2 entries:
  "abc" => 123
  "def" => 456

というわけで、中身を指定しながら宣言したい場合に、現在ver.0.3 / 0.4で統一して使用できる記法がないのでした。

どうしても統一したい場合には、以下のように先に空のDictをDict{K, V}()というかたちで宣言して、そこに値を追加する方法が良いと思う。

julia> my_dict = Dict{UTF8String, Int64}()
Dict{UTF8String,Int64} with 0 entries

julia> my_dict["キー"] = 123
123

julia> my_dict["別のキー"] = 456
456

julia> my_dict
Dict{UTF8String,Int64} with 2 entries:
  "別の… => 456
  "キー" => 123

julia> my_dict["キー"]
123

この方法は、ver.0.3 / ver.0.4共通で利用できる。

Dict関連の関数

ざっくりいって、pythonのdict関係と似た関数が用意されている。
Juliaはクラスの概念がないので、第一引数にDictを指定する。

詳しくはver.0.3のドキュメントおよび ver0.4のドキュメントを参照のこと。

よく使いそうな関数の例を以下にあげておく。

julia> get(dict, "unknown", "key not found")  # 見つからなかった時のデフォルト値が指定できる
"key not found"

julia> # キーが見つからなかった時の挙動は、関数でも指定できる(do構文)
       get(dict, "unknown") do
         println("key not found!")
       end
key not found!

julia> haskey(dict, "foo")   # キーがあるかどうかチェック
true

julia> delete!(dict, "foo")   # 指定したキーの項目を削除
Dict{ASCIIString,ASCIIString} with 2 entries:
  "bar" => "456"
  "baz" => "789"

julia> keys(dict)    # キーのイテレータを取得
KeyIterator for a Dict{ASCIIString,ASCIIString} with 2 entries. Keys:
  "bar"
  "baz"

julia> values(dict)    # 値のイテレータを取得
ValueIterator for a Dict{ASCIIString,ASCIIString} with 2 entries. Values:
  "456"
  "789"

get関数は、キーがみつからなかった時にdefault値を返すだけだけど、get!関数はみつからなかった場合は指定したキーdefault値またはdefault関数の実行結果を設定する。

julia> dict
Dict{ASCIIString,ASCIIString} with 3 entries:
  "bar" => "456"
  "baz" => "789"
  "foo" => "123"

julia> get!(dict, "NEW_KEY", "NEW_VALUE")
"NEW_VALUE"

julia> dict
Dict{ASCIIString,ASCIIString} with 4 entries:
  "bar"     => "456"
  "NEW_KEY" => "NEW_VALUE"
  "baz"     => "789"
  "foo"     => "123"

julia> get!(dict, "ANOTHER_KEY") do
         return "ANOTHER_VALUE"
       end
"ANOTHER_VALUE"

julia> dict
Dict{ASCIIString,ASCIIString} with 5 entries:
  "bar"         => "456"
  "NEW_KEY"     => "NEW_VALUE"
  "baz"         => "789"
  "ANOTHER_KEY" => "ANOTHER_VALUE"
  "foo"         => "123"

Set型

pythonのsetと同様、重複がないコレクション型がSet。
Arrayの重複削除くらいしか使わないかな、普段は。

julia> anyset = Set()    # 型を指定しないとAny型のSetになる
Set{Any}({})

julia> floatset = Set{Float64}()    # 型を指定する方法
Set{Float64}({})

julia> strset = Set(["foo", "bar", "baz"])    # 型を自動設定する方法
Set{ASCIIString}({"bar","baz","foo"})

julia> push!(strset, "NEW_VALUE")    # 新しい値の追加
Set{ASCIIString}({"NEW_VALUE","bar","baz","foo"})

julia> push!(strset, "foo")    # 同じ値は追加しても重複しない
Set{ASCIIString}({"NEW_VALUE","bar","baz","foo"})

julia> [v for v in strset]    # 配列への変換
4-element Array{Any,1}:
 "NEW_VALUE"
 "bar"      
 "baz"      
 "foo"

以上です。