如同大多數程式語言,Julia 提供了豐富多樣的集合型別。
有些集合天生就有順序(包括Vectors和更高維度的Arrays),因此我們可以依位置存取元素。
其他集合則是無序的(Sets、Dicts),元素必須透過鍵或值來存取。
最簡單的情況是,假設我們有一個由Ints組成的Vector,並想將它依遞增順序排序。
julia> v = [3, 2, 4, 1]
4-element Vector{Int64}:
3
2
4
1
julia> sort(v)
4-element Vector{Int64}:
1
2
3
4
# sort descending
julia> sort(v, rev=true)
4-element Vector{Int64}:
4
3
2
1
很簡單!
sort()這個函式的名稱淺顯易懂,而且看起來會回傳排序好的Vector。
開玩笑的! 學到課程大綱的這個階段,你或許已經(正確地)猜到,有些細節值得我們好好討論。
執行sort(v)之後,v會變成什麼?
julia> v
4-element Vector{Int64}:
3
2
4
1
julia> issorted(v)
false
原本的向量並沒有改變。
sort()是一個不會修改輸入的函式,它會回傳輸入的_副本_。
在許多情境下這是個好設計:我們有一個純函式,行為很容易推斷,即使在平行、分散式的程式碼中也是如此。
另一方面,假設我們有一個包含十億個字串的陣列,並想將它排序。
排序大概會很慢。 此外,我們還得複製所有內容(很慢!),並配置足夠的記憶體(好幾 GB!)來存放副本。
比較一下這段很類似的程式碼:
# v is as previously
julia> issorted(v)
false
julia> sort!(v)
4-element Vector{Int64}:
1
2
3
4
julia> v
4-element Vector{Int64}:
1
2
3
4
julia> issorted(v)
true
現在輸入的Vector會就地排序,不需複製,而原本的順序則會永久消失。
這樣做可能很有效率,也可能藏有 bug 並導致意想不到的副作用,還可能讓其他使用你程式碼的人感到困惑。 請謹慎選擇!
sort!()是「會修改」的函式,而且依照(非常強烈)的慣例,任何會修改輸入的函式,名稱後面都會加上!,提醒程式設計師注意。
如果要排序的元素是大型物件,改為操作它們的位置索引可能會更容易。
julia> langs = ["Julia", "C++", "Python", "Elixir"]
4-element Vector{String}:
"Julia"
"C++"
"Python"
"Elixir"
julia> inxs = sortperm(langs) # => Integers
4-element Vector{Int64}:
2
4
1
3
julia> langs[inxs] # sort the entire vector
4-element Vector{String}:
"C++"
"Elixir"
"Julia"
"Python"
julia> langs[inxs[1:2]] # slice the indices
2-element Vector{String}:
"C++"
"Elixir"
因為langs是一個 Vector,每個索引都是落在1:length(langs)範圍內的整數。
sortperm()的輸出是這些整數的一種排列,對應於輸入向量的排序結果。
你被一間為烏龜製作拼布被的公司聘為助理生產工程師。
訂單整天陸續進來,內容是一組顧客名稱或編號,以及訂購數量。 這些訂單會在早上、當天生產開始之前處理完畢。
只要當天的訂單全部完成,你就可以領全薪下班回家,所以大家都有動機盡可能縮短工作日。 由於每張訂單都由單一機器處理,先把最大的訂單排進去,就能把啟動工作的額外負擔推給較小的訂單,進而縮短整體生產時間。 因此,你決定提供一些排序函式來協助加快排程,讓你可以早點回家替烏龜織件毛衣。
如前所述,數量陣列需要由大到小排序。 由於這些陣列可能很大,排序應該以原地方式進行。 為了正確追蹤所有資料,你還需要能依照數量來排序顧客陣列,因此會需要一組數量的排序排列。
請撰寫一個函式sortquantity!(qty),它接受一個由Integer數量組成的Vector。
這個函式會以原地方式將輸入的qty由大到小排序,並回傳代表這個排序結果的排序排列。
julia> qty = [8, 9, 5, 6]
4-element Vector{Int64}:
8
9
5
6
julia> sortquantity!(qty)
4-element Vector{Int64}:
2
1
4
3
julia> qty # original vector is sorted in-place
4-element Vector{Int64}:
9
8
6
5
如上所述,你需要依照固定的順序追蹤顧客。
請撰寫一個函式sortcustomer(cust, srtperm),它接受一個顧客的Vector cust 和一個排序排列的Vector srtperm。
這個函式會回傳一個新的陣列,其中的顧客依照這個排序排列所指定的順序排列。
julia> cust = ["a", "b", "c"]
3-element Vector{String}:
"a"
"b"
"c"
julia> sortcustomer(cust, [2, 3, 1])
3-element Vector{String}:
"b"
"c"
"a"
julia> cust # original vector remains unsorted
3-element Vector{String}:
"a"
"b"
"c"
現在你可以把所有東西組合起來,幫那些烏龜保暖了! 你已經能正確排序數量和顧客,但還需要能還原出貨時的原始順序。 這可以透過反向排序排列來完成。
請撰寫一個函式production_schedule!(cust, qty),它接受一個顧客的Vector和一個數量的Vector。
這個函式會以原地方式將數量由大到小排序。
它會回傳一個新的顧客Vector,排序與數量相同,以及一個反向排序排列,可用來把已排序的向量還原成原始順序。
julia> cust, qty = ["a", "b", "c"], [2, 3, 1]
(["a", "b", "c"], [2, 3, 1])
julia> orderedcust, invperm = production_schedule!(cust, qty)
(["b", "a", "c"], [2, 1, 3])
julia> qty # has been sorted in-place
3-element Vector{Int64}:
3
2
1
julia> orderedcust[invperm], qty[invperm] # inverse permutation gives original ordering
(["a", "b", "c"], [2, 3, 1])