一般設定 random seed 的方式則像以下的程式碼:

1
2
using Random
Random.seed!(0)

所以我們可以得出一個隨機矩陣看起來像是這樣。

1
2
3
4
5
6
7
julia> rand(5, 5)
5×5 Matrix{Float64}:
0.823648 0.203477 0.585812 0.655448 0.469304
0.910357 0.0423017 0.539289 0.575887 0.0623676
0.164566 0.0682693 0.260036 0.868279 0.353129
0.177329 0.361828 0.910047 0.9678 0.767602
0.27888 0.973216 0.167036 0.76769 0.043141

如果我們要對每個執行緒都設定同樣的 random seed,那就可以在每個執行緒內直接設定即可。

1
2
3
4
5
Threads.@threads for i in 1:5
Random.seed!(0)
tid = Threads.threadid()
println(tid, ": ", rand(5, 5))
end

所以每個執行緒內印出來的矩陣就會像這樣。

1
2
3
4
5
5: [0.8236475079774124 0.20347655804192266 0.5858115517433242 0.6554484126999125 0.46930370935301835; 0.9103565379264364 0.042301665932029664 0.5392892841426182 0.5758873948500367 0.06236755817015882; 0.16456579813368521 0.06826925550564478 0.26003585026904785 0.8682787096942046 0.35312877270491705; 0.17732884646626457 0.3618283907762174 0.910046541351011 0.9677995536192001 0.767601895961374; 0.278880109331201 0.9732164043865108 0.16703619444214968 0.7676903325581188 0.043141023329413164]
2: [0.8236475079774124 0.20347655804192266 0.5858115517433242 0.6554484126999125 0.46930370935301835; 0.9103565379264364 0.042301665932029664 0.5392892841426182 0.5758873948500367 0.06236755817015882; 0.16456579813368521 0.06826925550564478 0.26003585026904785 0.8682787096942046 0.35312877270491705; 0.17732884646626457 0.3618283907762174 0.910046541351011 0.9677995536192001 0.767601895961374; 0.278880109331201 0.9732164043865108 0.16703619444214968 0.7676903325581188 0.043141023329413164]
4: [0.8236475079774124 0.20347655804192266 0.5858115517433242 0.6554484126999125 0.46930370935301835; 0.9103565379264364 0.042301665932029664 0.5392892841426182 0.5758873948500367 0.06236755817015882; 0.16456579813368521 0.06826925550564478 0.26003585026904785 0.8682787096942046 0.35312877270491705; 0.17732884646626457 0.3618283907762174 0.910046541351011 0.9677995536192001 0.767601895961374; 0.278880109331201 0.9732164043865108 0.16703619444214968 0.7676903325581188 0.043141023329413164]
3: [0.8236475079774124 0.20347655804192266 0.5858115517433242 0.6554484126999125 0.46930370935301835; 0.9103565379264364 0.042301665932029664 0.5392892841426182 0.5758873948500367 0.06236755817015882; 0.16456579813368521 0.06826925550564478 0.26003585026904785 0.8682787096942046 0.35312877270491705; 0.17732884646626457 0.3618283907762174 0.910046541351011 0.9677995536192001 0.767601895961374; 0.278880109331201 0.9732164043865108 0.16703619444214968 0.7676903325581188 0.043141023329413164]
1: [0.8236475079774124 0.20347655804192266 0.5858115517433242 0.6554484126999125 0.46930370935301835; 0.9103565379264364 0.042301665932029664 0.5392892841426182 0.5758873948500367 0.06236755817015882; 0.16456579813368521 0.06826925550564478 0.26003585026904785 0.8682787096942046 0.35312877270491705; 0.17732884646626457 0.3618283907762174 0.910046541351011 0.9677995536192001 0.767601895961374; 0.278880109331201 0.9732164043865108 0.16703619444214968 0.7676903325581188 0.043141023329413164]

留言與分享

多重分派(multiple dispatch)是 Julia 語言中相當重要的程式典範。多重分派的例子在 Julia 的套建中相當多見,其中一個應用就是用來取代型別檢查。

在一般的程式中有相當多的型別檢查的片段,在 Julia 中可能會寫成這樣:

1
2
3
4
5
6
7
8
9
function foo(x)
if x isa Int
# do something
elseif x isa String
# do another things
elseif x isa Float64
# do other things
end
end

在 Julia 中這樣寫當然是可以,但是這樣就失去了用 Julia 的優勢,可以將他用多重分派進行改寫。

1
2
3
4
5
6
7
8
9
10
11
function foo(x::Int)
# do something
end

function foo(x::String)
# do another things
end

function foo(x::Float64)
# do other things
end

看起來好像只是將他分成三隻函式而已,沒什麼了不起的吧?

不不不,光是將他分成三隻函式,就足以讓 if...else 結構中的程式馬可以分別被編譯,成為效能更好的程式碼。在編譯的過程中,也由於有充足的引數型別的資訊,所以會編譯出更好的程式。

更值得一提的是,在軟體工程上也是有好處的,他符合軟體工程的開放封閉原則,對新增功能開放、對修改現有功能關閉,要支援新的型別只需要增加新的函式,並不用去修改現有的 if...else 結構,可以避免新增功能的同時去動到舊有的功能。

在演算法上也可以依據不同的型別做分離,可以將他看能完全不同的演算法,演算法之間不會混雜在一起。

所以這就是善用 Julia 的多重分派來改寫型別檢查的範例。

留言與分享

一般我們會在函式的引數上加上參數,這樣可以讓函式去推論引數的型別,這樣有多種用途,其中之一當然是增進效能。

1
2
3
4
5
julia> foo(a::T) where {T} = zero(T)
foo (generic function with 1 method)

julia> foo(5)
0

那我們有沒有辦法對回傳值的型別做參數化呢?

有的,可以這樣做。

1
2
3
4
5
julia> (foo(a::T)::T) where {T} = zero(T)
foo (generic function with 1 method)

julia> foo(5)
0

一般要標記回傳值的型別,要寫在整個 function signature 的後面,也就是

1
foo(a)::Int

如果要將他標記為參數,那就需要寫成

1
(foo(a)::T) where {T}

這樣就寫出來啦!!

留言與分享

有鑑於 Debugger.jl 的說明文件有點難懂,就自己來寫一篇教學。

Debugger.jl 是在 Julia 上,由官方開發及維護的除錯器(debugger),debugger 如同其他語言的 debugger 一樣,可以預先設定斷點(breakpoint)、在丟出例外(exception)時停下來、逐行執行及檢查變數等等功能。這篇文章會示範最基礎的 debugger 的用法。

繼續閱讀

常常開發者會需要在,使用者使用不同的語言版本或是套件版本,做不同的處置,有可能是安裝不同的函式庫,或是設定不同的參數。這時候要在程式中自動取得目前的語言版本或是特定套件的版本就很重要。Julia 語言本身就有內建版本字串可以使用,它可以用來比較版本的差異或是大小。

繼續閱讀

在 v1.6 版中,Julia 有大幅度的效能提昇,效能提昇的部份著重在於預編譯(precompilation)以及編譯的效能改進,而 v1.6 版將成為下一個長期支援(long-term support, LTS)的版本。

根據官方部落格,本次版本更新引進了眾多新的技術,包含 parallel precompilation、消除重複編譯、降低編譯器的 latency、加速二進位檔案的載入,以及改善 stacktrace 的格式等等重大的功能。那我們就來詳細看看有哪些更新吧!

繼續閱讀

Julia 作為一個新興的語言,他有眾多的優點,他不僅執行快速,而且像 python 一樣,是動態語言非常容易上手好寫。Julia 本身的套件管理系統也具有足夠高的再現性(reproducible),並且自動安裝內含編譯過的函式庫檔案。作為一個泛用性以及開源的程式語言,這些特色帶來許多優點。

隨著今日科學社群不斷的擴大,科學計算與數值計算相關的軟體也不斷的發展,Julia 作為一個以科學計算為目的的語言,不僅融合了現代的科學計算工具、新穎的演算法、優異的性能,以及平易近人的語法。由於 Julia 優異的語法設計,搭配多重分派以及型別系統,讓獨到的即時編譯(just-in-time compilation)變得自然,型別推斷讓 Julia 的速度得以提升。

繼續閱讀

對於 Julia 比較不熟悉的使用者或開發者,要怎麼定函式的參數型別是一個大學問。

1
2
3
function average(xs)
return sum(xs)/length(xs)
end

例如以上的 average 函式,xs 要定成什麼樣的型別比較好?

可能有人會以他所測試的情境去定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
julia> xs = rand(10)
10-element Array{Float64,1}:
0.8261912048684596
0.4466692353358077
0.6337754306737924
0.1297961522759261
0.4699412442087936
0.6194816490253636
0.8258926319092839
0.05094839501664228
0.21008570113108194
0.16250331333741275

julia> function average(xs::Array{Float64,1})
return sum(xs)/length(xs)
end
average (generic function with 1 method)

julia> average(xs)
0.4375284957782564
繼續閱讀

Yueh-Hua Tu

目標是計算生物學家!
Systems Biology, Computational Biology, Machine Learning
Julia Taiwan 發起人


研發替代役研究助理


Taiwan