介紹 Julia/字典和集合
到目前為止介紹的許多函式都已在陣列(和元組)上執行。但是陣列只是集合型別之一。Julia 還有其他型別。
一個簡單的查詢表是組織多種型別資料的有用方式:給定一個單一的資訊,例如數字、字串或符號,稱為**鍵**,相應的**值**是什麼?為此,Julia 提供了 Dictionary 物件,簡稱 Dict。它是一種“關聯集合”,因為它將鍵與值關聯。
您可以使用以下語法建立一個簡單的字典
julia> dict = Dict("a" => 1, "b" => 2, "c" => 3)
Dict{String,Int64} with 3 entries:
"c" => 3
"b" => 2
"a" => 1
dict 現在是一個字典。鍵是“a”、“b”和“c”,相應的 value 是 1、2 和 3。=> 運算子稱為 Pair() 函式。在字典中,鍵總是唯一的 - 您不能有兩個具有相同名稱的鍵。
如果您事先知道鍵和值的型別,您可以在 Dict 關鍵字之後用大括號指定它們(並且可能應該這樣做)
julia> dict = Dict{String,Integer}("a"=>1, "b" => 2)
Dict{String,Integer} with 2 entries:
"b" => 2
"a" => 1
您還可以使用生成器/ 推導 語法來建立字典
julia> dict = Dict(string(i) => sind(i) for i = 0:5:360)
Dict{String,Float64} with 73 entries:
"320" => -0.642788
"65" => 0.906308
"155" => 0.422618
⋮ => ⋮
使用以下語法建立一個型別化的空字典
julia> dict = Dict{String,Int64}()
Dict{String,Int64} with 0 entries
或者您可以省略型別,並獲得一個非型別化的字典
julia> dict = Dict()
Dict{Any,Any} with 0 entries
使用 for 迴圈建立字典條目有時很有用
files = ["a.txt", "b.txt", "c.txt"]
fvars = Dict()
for (n, f) in enumerate(files)
fvars["x_$(n)"] = f
end
這是建立儲存在字典中的“變數”集的一種方法
julia> fvars
Dict{Any,Any} with 3 entries:
"x_1" => "a.txt"
"x_2" => "b.txt"
"x_3" => "c.txt"
要獲取值,如果您有鍵
julia> dict = Dict("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5)
julia> dict["a"]
1
如果鍵是字串。或者,如果鍵是符號
julia> symdict = Dict(:x => 1, :y => 3, :z => 6)
Dict{Symbol,Int64} with 3 entries:
:z => 6
:x => 1
:y => 3
julia> symdict[:x] 1
或者如果鍵是整數
julia> intdict = Dict(1 => "one", 2 => "two", 3 => "three")
Dict{Int64,String} with 3 entries:
2 => "two"
3 => "three"
1 => "one"
julia> intdict[2] "two"
您可以使用 get() 函式,並在特定鍵沒有值的情況下提供一個安全預設值
julia> dict = Dict("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5)
julia> get(dict, "a", 0) 1 julia> get(dict, "Z", 0) 0
如果您不希望 get() 提供預設值,請使用 try...catch 塊
try
dict["Z"]
catch error
if isa(error, KeyError)
println("sorry, I couldn't find anything")
end
end
sorry, I couldn't find anything
要更改分配給現有鍵的值(或將值分配給以前未見過的鍵)
julia> dict["a"] = 10 10
字典的鍵必須是唯一的。這個字典中始終只有一個名為 a 的鍵,因此當您將值分配給已經存在的鍵時,您不是在建立新的鍵,而只是修改現有的鍵。
要檢視字典是否包含鍵,請使用 haskey()
julia> haskey(dict, "Z") false
要檢查鍵/值對是否存在
julia> in(("b" => 2), dict)
true
要向字典新增新的鍵和值,請使用以下方法
julia> dict["d"] = 4 4
您可以使用 delete!() 從字典中刪除鍵
julia> delete!(dict, "d")
Dict{String,Int64} with 4 entries:
"c" => 3
"e" => 5
"b" => 2
"a" => 1
您會注意到字典似乎沒有以任何方式排序 - 至少,鍵沒有特定的順序。這是由於它們的儲存方式,您無法就地對它們進行排序。(但請參閱下面的 排序。)
要獲取所有鍵,請使用 keys() 函式
julia> dict = Dict("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5);
julia> keys(dict)
Base.KeySet for a Dict{String,Int64} with 5 entries. Keys:
"c"
"e"
"b"
"a"
"d"
結果是一個迭代器,它只有一個工作:逐鍵迭代字典
julia> collect(keys(dict))
5-element Array{String,1}:
"c"
"e"
"b"
"a"
"d"
julia> [uppercase(key) for key in keys(dict)]
5-element Array{Any,1}:
"C"
"E"
"B"
"A"
"D"
這使用列表推導形式([ 新元素 for 迴圈變數 in 迭代器 ]),並且每個新元素都被收集到陣列中。另一種方法是
julia> map(uppercase, collect(keys(dict)))
5-element Array{String,1}:
"C"
"E"
"B"
"A"
"D"
要檢索所有值,請使用 values() 函式
julia> values(dict)
Base.ValueIterator for a Dict{String,Int64} with 5 entries. Values:
3
5
2
1
4
如果您想遍歷字典並處理每個鍵/值,您可以利用字典本身是可迭代物件的事實
julia> for kv in dict println(kv) end "c"=>3 "e"=>5 "b"=>2 "a"=>1 "d"=>4
其中 kv 是一個元組,依次包含每個鍵/值對。
或者您可以這樣做
julia> for k in keys(dict)
println(k, " ==> ", dict[k])
end
c ==> 3
e ==> 5
b ==> 2
a ==> 1
d ==> 4
更好的是,您可以使用鍵/值元組來進一步簡化迭代
julia> for (key, value) in dict
println(key, " ==> ", value)
end
c ==> 3
e ==> 5
b ==> 2
a ==> 1
d ==> 4
以下是一個示例
for tuple in Dict("1"=>"Hydrogen", "2"=>"Helium", "3"=>"Lithium")
println("Element $(tuple[1]) is $(tuple[2])")
end
Element 1 is Hydrogen
Element 2 is Helium
Element 3 is Lithium
(注意字串插值運算子 $。這使您可以在字串中使用變數的名稱,並在列印字串時獲取變數的值。您可以使用 $() 在字串中包含任何 Julia 表示式。)
因為字典不按任何特定順序儲存鍵,所以您可能想要將字典輸出到排序陣列中,以按順序獲取專案
julia> dict = Dict("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6)
Dict{String,Int64} with 6 entries:
"f" => 6
"c" => 3
"e" => 5
"b" => 2
"a" => 1
"d" => 4
julia> for key in sort(collect(keys(dict)))
println("$key => $(dict[key])")
end
a => 1
b => 2
c => 3
d => 4
e => 5
f => 6
如果您確實需要始終保持排序的字典,您可以使用 DataStructures.jl 包中的 SortedDict 資料型別(安裝後)。
julia> import DataStructures
julia> dict = DataStructures.SortedDict("b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6)
DataStructures.SortedDict{String,Int64,Base.Order.ForwardOrdering} with 5 entries:
"b" => 2
"c" => 3
"d" => 4
"e" => 5
"f" => 6
julia> dict["a"] = 1 1
julia> dict
DataStructures.SortedDict{String,Int64,Base.Order.ForwardOrdering} with 6 entries:
"a" => 1
"b" => 2
"c" => 3
"d" => 4
"e" => 5
"f" => 6
Julia 的最新版本會為您排序字典
julia> dict = Dict("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6)
Dict{String,Int64} with 6 entries:
"f" => 6
"c" => 3
"e" => 5
"b" => 2
"a" => 1
"d" => 4
julia> sort(dict)
OrderedCollections.OrderedDict{String,Int64} with 6 entries:
"a" => 1
"b" => 2
"c" => 3
"d" => 4
"e" => 5
"f" => 6
字典的一個簡單應用是統計文字中每個詞出現的次數。每個詞都是一個鍵,鍵的值是該詞在文字中出現的次數。
讓我們統計一下福爾摩斯故事中的詞語。我已經從優秀的 Project Gutenberg 下載了文字,並將它們儲存在一個名為“sherlock-holmes-canon.txt”的檔案中。要從 canon 中載入的文字建立單詞列表,我們將使用正則表示式拆分文字,並將每個單詞轉換為小寫。(可能還有更快的辦法。)
julia> f = open("sherlock-holmes-canon.txt")
julia> wordlist = String[]
julia> for line in eachline(f)
words = split(line, r"\W")
map(w -> push!(wordlist, lowercase(w)), words)
end
julia> filter!(!isempty, wordlist)
julia> close(f)
wordlist 現在是一個包含近 700,000 個單詞的陣列
julia> wordlist[1:20]
20-element Array{String,1}:
"THE"
"COMPLETE"
"SHERLOCK"
"HOLMES"
"Arthur"
"Conan"
"Doyle"
"Table"
"of"
"contents"
"A"
"Study"
"In"
"Scarlet"
"The"
"Sign"
"of"
"the"
"Four"
"The"
要儲存單詞和單詞計數,我們將建立一個字典
julia> wordcounts = Dict{String,Int64}()
Dict{String,Int64} with 0 entries
要構建字典,請遍歷單詞列表,並使用 get() 查詢當前計數(如果有)。如果該詞已經出現過,則可以增加計數。如果該詞以前從未見過,則 get() 的回退第三個引數確保不存在不會導致錯誤,並且將儲存 1。
for word in wordlist
wordcounts[word]=get(wordcounts, word, 0) + 1
end
現在,您可以在 wordcounts 字典中查詢單詞,並找出它們出現的次數
julia> wordcounts["watson"] 1040 julia> wordcounts["holmes"] 3057 julia> wordcounts["sherlock"] 415 julia> wordcounts["lestrade"] 244
字典未排序,但您可以對字典使用 collect() 和 keys() 函式來收集鍵,然後對它們進行排序。在一個迴圈中,您可以按字母順序處理字典
for i in sort(collect(keys(wordcounts)))
println("$i, $(wordcounts[i])")
end
000, 5
1, 8
10, 7
100, 4
1000, 9
104, 1
109, 1
10s, 2
10th, 1
11, 9
1100, 1
117, 2
117th, 2
11th, 1
12, 2
120, 2
126b, 3
⋮
zamba, 2
zeal, 5
zealand, 3
zealous, 3
zenith, 1
zeppelin, 1
zero, 2
zest, 3
zig, 1
zigzag, 3
zigzagged, 1
zinc, 3
zion, 2
zoo, 1
zoology, 2
zu, 1
zum, 2
â, 41
ã, 4
但您如何找出最常見的詞語呢?一種方法是使用 collect() 將字典轉換為元組陣列,然後透過檢視每個元組的最後一個值來對陣列進行排序
julia> sort(collect(wordcounts), by = tuple -> last(tuple), rev=true)
19171-element Array{Pair{String,Int64},1}:
("the",36244)
("and",17593)
("i",17357)
("of",16779)
("to",16041)
("a",15848)
("that",11506)
⋮
("enrage",1)
("smuggled",1)
("lounges",1)
("devotes",1)
("reverberated",1)
("munitions",1)
("graybeard",1)
要檢視前 20 個單詞
julia> sort(collect(wordcounts), by = tuple -> last(tuple), rev=true)[1:20]
20-element Array{Pair{String,Int64},1}:
("the",36244)
("and",17593)
("i",17357)
("of",16779)
("to",16041)
("a",15848)
("that",11506)
("it",11101)
("in",10766)
("he",10366)
("was",9844)
("you",9688)
("his",7836)
("is",6650)
("had",6057)
("have",5532)
("my",5293)
("with",5256)
("as",4755)
("for",4713)
以類似的方式,您可以使用 filter() 函式來查詢例如所有以“k”開頭且出現次數少於四次的單詞
julia> filter(tuple -> startswith(first(tuple), "k") && last(tuple) < 4, collect(wordcounts))
73-element Array{Pair{String,Int64},1}:
("keg",1)
("klux",2)
("knifing",1)
("keening",1)
("kansas",3)
⋮
("kaiser",1)
("kidnap",2)
("keswick",1)
("kings",2)
("kratides",3)
("ken",2)
("kindliness",2)
("klan",2)
("keepsake",1)
("kindled",2)
("kit",2)
("kicking",1)
("kramm",2)
("knob",1)
字典可以儲存多種不同型別的值。例如,這裡有一個字典,它的鍵是字串,而值是點陣列(假設 Point 型別已經定義)。例如,這可以用來儲存描述字母的圖形形狀(其中一些有 2 個或更多個迴圈)
julia> p = Dict{String, Array{Array}}()
Dict{String,Array{Array{T,N},N}}
julia> p["a"] = Array[[Point(0,0), Point(1,1)], [Point(34, 23), Point(5,6)]]
2-element Array{Array{T,N},1}:
[Point(0.0,0.0), Point(1.0,1.0)]
[Point(34.0,23.0), Point(5.0,6.0)]
julia> push!(p["a"], [Point(34.0,23.0), Point(5.0,6.0)])
3-element Array{Array{T,N},1}:
[Point(0.0,0.0), Point(1.0,1.0)]
[Point(34.0,23.0), Point(5.0,6.0)]
[Point(34.0,23.0), Point(5.0,6.0)]
或者建立一個包含一些已知值的字典
julia> d = Dict("shape1" => Array [ [ Point(0,0), Point(-20,57)], [Point(34, -23), Point(-10,12) ] ])
Dict{String,Array{Array{T,N},1}} with 1 entry:
"shape1" => Array [ [ Point(0.0,0.0), Point(-20.0,57.0)], [Point(34.0,-23.0), Point(-10.0,12.0) ] ]
向第一個陣列新增另一個數組
julia> push!(d["shape1"], [Point(-124.0, 37.0), Point(25.0,32.0)])
3-element Array{Array{T,N},1}:
[Point(0.0,0.0), Point(-20.0,57.0)]
[Point(34.0,-23.0), Point(-10.0,12.0)]
[Point(-124.0,37.0), Point(25.0,32.0)]
集合是元素的集合,就像陣列或字典一樣,沒有重複的元素。
集合與其他型別集合的兩個重要區別是:在集合中,你只能擁有每個元素的一個,並且在集合中,元素的順序並不重要(而陣列可以包含一個元素的多個副本,並且它們的順序會被記住)。
你可以使用 Set 建構函式建立空集合
julia> colors = Set()
Set{Any}({})
與 Julia 中的其他地方一樣,你可以指定型別
julia> primes = Set{Int64}()
Set(Int64)[]
你可以在一步內建立並填充集合
julia> colors = Set{String}(["red","green","blue","yellow"])
Set(String["yellow","blue","green","red"])
或者你可以讓 Julia "猜測型別"
julia> colors = Set(["red","green","blue","yellow"])
Set{String}({"yellow","blue","green","red"})
許多用於陣列的函式也適用於集合。例如,向集合新增元素有點像向陣列新增元素。你可以使用 push!()
julia> push!(colors, "black")
Set{String}({"yellow","blue","green","black","red"})
但你不能使用 pushfirst!(),因為它只適用於具有 "第一個" 概念的事物,比如陣列。
如果你嘗試向集合中新增已經存在的元素會發生什麼?什麼也不會發生。你不會新增副本,因為它是一個集合,而不是一個數組,並且集合不儲存重複的元素。
要檢視集合中是否包含某個元素,你可以使用 in()
julia> in("green", colors)
true
你可以對集合執行一些標準操作,即找到它們的 **並集**、**交集** 和 **差集**,分別使用函式 union()、intersect() 和 setdiff()
julia> rainbow = Set(["red","orange","yellow","green","blue","indigo","violet"]) Set(String["indigo","yellow","orange","blue","violet","green","red"])
兩個集合的並集是包含所有屬於這兩個集合的元素的集合。結果是另一個集合,因此你不能有兩個 "yellow",即使每個集合都包含一個 "yellow"
julia> union(colors, rainbow) Set(String["indigo","yellow","orange","blue","violet","green","black","red"])
兩個集合的交集是包含屬於這兩個集合的每個元素的集合
julia> intersect(colors, rainbow) Set(String["yellow","blue","green","red"])
兩個集合的差集是包含第一個集合中存在的元素,但不在第二個集合中的元素的集合。這次,你提供集合的順序很重要。setdiff() 函式找到第一個集合 colors 中存在但不在第二個集合 rainbow 中的元素
julia> setdiff(colors, rainbow) Set(String["black"])
在陣列和集合上執行的函式有時也適用於字典和其他集合。例如,一些集合操作可以應用於字典,而不僅僅是集合和陣列
julia> d1 = Dict(1=>"a", 2 => "b")
Dict{Int64,String} with 2 entries:
2 => "b"
1 => "a"
julia> d2 = Dict(2 => "b", 3 =>"c", 4 => "d")
Dict{Int64,String} with 3 entries:
4 => "d"
2 => "b"
3 => "c"
julia> union(d1, d2)
4-element Array{Pair{Int64,String},1}:
2=>"b"
1=>"a"
4=>"d"
3=>"c"
julia> intersect(d1, d2)
1-element Array{Pair{Int64,String},1}:
2=>"b"
julia> setdiff(d1, d2)
1-element Array{Pair{Int64,String},1}:
1=>"a"
請注意,結果作為 Pair 陣列返回,而不是作為字典返回。
我們已經看到過用於陣列的 filter()、map() 和 collect() 等函式也適用於字典
julia> filter((k, v) -> k == 1, d1)
Dict{Int64,String} with 1 entry:
1 => "a"
有一個 merge() 函式可以合併兩個字典
julia> merge(d1, d2)
Dict{Int64,String} with 4 entries:
4 => "d"
2 => "b"
3 => "c"
1 => "a"
findmin() 函式可以在字典中找到最小值,並返回該值及其鍵。
julia> d1 = Dict(:a => 1, :b => 2, :c => 0)
Dict{Symbol,Int64} with 3 entries:
:a => 1
:b => 2
:c => 0
julia> findmin(d1)
(0, :c)