新語言不焦慮!這裡有一份Julia極簡入門教程

2021-03-06 AI前線
AI 前線導讀:前不久,AI 前線發布了一篇名為《集 Python、C++、R 為一體!Julia 1.0 重磅發布》的文章,為各位讀者報導了有關新興程式語言 Julia 1.0 的有關情況,當時有不少讀者在評論區留言:有沒有教程?今天我們就為有這個需求的讀者朋友們帶來 Julia 極簡入門教程,希望可以幫到你!

更多優質內容請關注微信公眾號「AI 前線」(ID:ai-front)

沒有銀彈,Julia 的特性更多不會有 Python 好學。但是不用擔心,上手是非常容易的。之前因為準備用戶見面會(Julia User Meet Up)準備了一個教程,這個教程很大一部分來自於:

https://github.com/Roger-luo/TutorialZH.jl

但是因為不是所有的依賴都支持了 1.0,所以這個教程會在下月更新一次。這裡僅僅是介紹一些基本語法,幫助大家快速上手。這個教程以及集智俱樂部的視頻:

https://www.bilibili.com/video/av28248187/

都可以任意轉載和修改。希望註明出處,也希望可以幫忙宣傳一下中文社區(當然這也並不是強制的)。

知乎上的話 Julia 有很多專欄,這個專欄更新的比較勤可以看看,也歡迎大家給這個專欄投稿:

https://zhuanlan.zhihu.com/julia

然後書的話,我有意願之後等中文社區人多一些組織一個開源圖書的項目,但是這個可能最近還實現不了,一個是我精力有限,第二是人手不足。我自己的專欄 Half Integer 是當 blog 用的,因為我也使用很多別的語言和技術,不會單獨分享 Julia 的內容,此外我也更傾向於寫一些更幹的東西。

然後如果你英文可以的話,有很多很棒的英文資料還有課程,我推薦你到這裡看看:

https://julialang.org/learning/

我首先推薦的是中文和英文的 discourse 論壇,它們的連結也都可以在官網找到,地址分別如下: 

中文論壇:https://discourse.juliacn.com/

英文論壇(這個人比較多):https://discourse.julialang.org/

然後你可以加 Julia 的其他社區:https://julialang.org/community/

也甚至可以直接去 GitHub 提交 issue,整個 Julia 語言的開發都是在 GitHub 上的:https://github.com/julialang/julia/issues

然後你也可以在知乎上提問,知乎上也有很多 Julia 的深度用戶(雖然還很少)。

最後你也可以加我們中文社區的 QQ 群,但是這是最不推薦的,因為 QQ 沒有代碼高亮,也不能像 slack 一樣支持各種方便技術討論的特性。一般 QQ 主要用於討論文檔翻譯工作的一些問題和社區建設。對於比較緊急的問題希望你可以在 discourse(中英文皆可)上發了帖子之後把連結發到 QQ 群裡來。

中文論壇目前由於域名備案還沒有完成(將會暫時掛靠在集智俱樂部名下)也還沒有配置 CDN,將會從香港跳轉,可能第一次訪問速度較慢,之後就好了,這是正常的我們也在慢慢解決這個問題。

Julia 官方團隊也在尋找怎麼加速國內訪問官網 julialang.org 的問題,我們日後也會考慮直接使用 julialang.org 。包括在境內維護鏡像下載等。但是因為目前真的非常缺乏人手,也希望大家可以參與到社區建設裡來。

中文社區是受到 Julia 開發團隊支持的眾多本地化組織之一,非盈利是基本準則。值得自豪的是,在 Julia 只有 0.3 的時候在 24 個貢獻者的努力下,就有了 0.3 的中文文檔。1.0 的中文文檔也正在進行中,我們也利用 Julia 的文檔系統嘗試支持 doc string。早期的成員裡有 Jiahao Chen,Yichao Yu,i2300 等 Julia 團隊的成員。 

JuliaCN 目前暫時不接受任何個人捐贈(因為這可能並不合法),但是如果你願意資助 Julia 語言的發展,可以通過官網的捐贈按鈕進行捐贈官網的地址在這裡,但是也希望對 Julia 語言感興趣的公司和機構能夠幫助這樣一個真正開源的,由社區成員自發組織起來的組織成長起來,雖然發起人已經不知道是誰了(並不是我),但是目前具體合作事宜都可以聯繫我。也非常希望有更多的機構可以贊助我們的(甚至是接下來的)活動和伺服器等開支。如果有 Julia 的招聘崗位也歡迎來社區做廣告。

這兩天的媒體報導可能讓一些人有了恐慌,但是我現在有一個誠懇的建議就是如果你完全沒有編程基礎,時間也不多的話(時間多了不是想學啥學啥),我建議你先學一下 Python,這並不衝突,因為 Julia 的語法本書和 Python 很像,1.0 之後也專門增加了一些 feature 幫助你更好地從 Python 轉向 Julia。此外因為 Julia 剛剛有了第一個長期支持版本,這還不意味著這個語言已經完全成熟,我想此時的 Julia 更像是彼時的 Python 2.0,還有很長一段路要走,但是已經非常的有前景。

那麼什麼人我會建議學習 Julia 呢?或者 Julia 在什麼場景下也許能夠有優勢呢?我個人的體驗是以下這裡一類:

之前使用 Python 但是因為性能問題,經常需要使用 numba/Cython/C API/ctypes/etc. 等方式進行優化的人。Julia 或許能夠幫助你解決兩語言問題,並且獲得可讀性更好,更容易維護的代碼。

之前使用 MATLAB,但是被一些付費功能困擾的用戶(MATLAB 2018 也是不錯的,但是要支持正版哈)

之前使用 Fortran 和 R 的用戶,強烈建議使用 Julia(可以結合著用也,FFI 是很不錯的)

之前使用 Sage/Octave 的用戶,不妨嘗試一下這個新玩意兒

之前使用 Mathematica 但是想開始做一些數值的用戶,Mathematica 不是不能做數值,也可以調用 C/C++ 但是 Julia 不妨是相比其它工具更平滑的選擇。

如果你之前的工作僅僅使用 Python 就足以勝任,那麼不必著急,也不必恐慌,不妨在感興趣的時候試試這個新東西,但是也完全可以等到 Julia 被大規模使用的時候再跟進。實際上從一開始像 MXNet 這樣的深度學習框架就官方支持了,這些框架的 Python 用戶轉移過來也並不是什麼難事,但是如果你無須高度定製自己的程序,那麼其實不會體會到什麼不同和優勢。

此外,也要再三聲明,雖然 Julia 可以寫出高性能的代碼,但是寫出高性能的代碼這件事情本身就很困難。雖然寫起來像 Python,運行速度像 C 是我們的夢想,但是在現在這個階段,並不是隨便寫一段 Julia 代碼就真的能達到 C 的。Julia 只是給你提供了充分優化的空間,和(達到高性能代碼的)相對容易的編程體驗。

Julia 目前因為官網的伺服器只有 AWS s3(他們也很窮)。所以國內的一些地區下載速度很慢:

https://julialang.org/downloads/

大家可以試一試,然後也可以去 Julia Computing 公司提供的 Julia 全家桶(你可以把它理解為 Julia 版本的 Anaconda),最左邊的 JuliaPro 是免費的:

https://juliacomputing.com/

之前浙大的 LUG 搭建了一個鏡像,但是維護的同學最近有一些忙,所以目前還沒有更新到 1.0。但是其實你如果無法從以上途徑下載,那麼從境內的源裡下載 Julia 0.6 也其實並不影響你先熟悉一些基本語法(這是這個教程的主要目的),境內的源的下載地址在這裡:

http://juliacn.com/downloads/

我們也會儘快更新。

然後還有一個叫做 Julia Box 的雲服務很方便可以使用,裡面有很多教程,都是 jupyter notebook,打開即用,全部都是在線的不用安裝。但是唯一的缺點就是國內可能不一定能夠正常訪問到。

http://juliabox.com

Julia 語言的社區不夠大,此外由於不是像 rust 這樣的靜態編譯語言,也不是像 CPython 這樣的解釋型編譯器,在啟動的時候有比較明顯的 overhead,這個問題一直在優化(REPL 的啟動時間已經從曾經的 1.0s 到了現在的 0.2s,依然和 IPython 這樣的有明顯差距),有 PL 的朋友私下和我說是 LLVM 的 JIT 不是那麼好(像 nodejs 的 V8 這個問題就不是很明顯)

所以在這個階段選擇一個合適的開發工具是非常必要的。目前支持最好,bug 最少的是 Atom 上的 Juno 插件,如果你下載 Julia Pro 那麼會自帶這個編輯器。如果你想選擇手動安裝,那麼可以在這裡下載 Atom:

https://atom.io/

http://docs.junolab.org/latest/man/installation.html

或者我也推薦你安裝 IJulia 之後,使用 jupyter notebook 和 jupyter lab 進行開發。

其它的平臺也有支持,例如 Jetbrain 的各個 IDE 都可以使用由 @考古學家千裡冰封等開發的插件。VS code 也有 Julia 插件,以及 Vim 也是有支持的。但是他們都還沒有支持逐行執行和單獨執行某一塊代碼的功能,這對於本身被設計得很像 Mathematica 的 Julia 來說沒有執行一個 cell 的支持開發起來會時常被 JIT 的預熱時間所困擾。

然後為了克服 JIT 的預熱,避免重複啟動編譯器。如果你不重新定義(re-define)類型的話,可以試試 Revise.jl :

https://github.com/timholy/Revise.jl

這是一個用於熱加載 Julia 代碼的工具,1.0 已經支持方法(method)的刪除了。所以也能夠方便你的開發。

其實和 Python 一樣,在我日常使用中,作為動態語言,以及因為語法本身適合分塊執行,我其實很少會用到斷點和專門的 debugger,此外雖然有相關的包,在 1.0 的編譯器裡也為未來加入 debugger 提供了相關功能,但是目前還沒有完善,你也許可以試試(但是我不推薦):

https://github.com/Keno/Gallium.jl

https://github.com/timholy/Rebugger.jl

Julia 有一個由社區維護的網站用來幫助你從 1900 多個包裡找出符合你需求的 Julia 包:

https://juliaobserver.com/

一般來說用比較新的,star 比較多的包會好一些。然後如果你覺得某個包不錯,也請在 GitHub 上給一個 star。

當你下載好了 Julia 之後,不論是 Julia Pro 還是單獨的 Julia 編譯器,你都可以先打開一個 REPL(交互式編程環境),類似於 IPython 之於 Python,Julia 的 REPL 支持了基本的代碼高亮,文檔查看等功能。但是 Julia 的 REPL 更強大(這件事稍後再說)。

雙擊 Julia 的三色圖標,就能打開 REPL。在 Atom 裡面的話在左上角有 Julia 一欄,點擊裡面的 open terminal 即可。

下載好以後去找到 bin 文件夾,然後把它加入你的 PATH 環境變量裡去,以後就可以在命令行裡直接通過 julia 命令啟動 REPL。

我相信你們是專業的,請閱讀官網的教程吧。注意超算用戶不用要求管理員安裝也可以安裝到自己的用戶目錄下的,設置好環境變量即可。然後有一些超算(比如中國科學技術大學的超算中心)Julia 編譯器是很早就裝好的,但是可能使用 module load 加載。

運行 Julia 的程序總的來說可以有三種方式(其實原理上它們都基本是等價的)

執行一個 Julia 腳本,和其它 Julia 語言一樣,你可以用如下命令執行 Julia 腳本,一般來說 Julia 腳本都以 .jl 作為擴展名。

julia script.jl

這個執行出來是沒有報錯高亮的,需要顏色請用以下命令執行

julia --color=yes script.jl

如果直接啟動 Julia 會進入到 REPL 裡去

julia

你會看到

也可以在這裡運行 Julia 命令。

在 REPL 裡面可以直接查文檔,按 ?就會跳到 help 模式,在 0.7 之後(包括 1.0),按 ] 就會進入 pkg 模式,在這個模式下按 ?就會顯示相關文檔

查看具體某個命令的文檔可以

安裝包在 0.7 之後都用 pkg 模式來安裝,因為這個更方便,但是和 0.6 一樣,如果你想使用代碼來安裝也是可以的,但是在 0.7 之後需要加載 Pkg 模塊(0.6 不用)

using Pkg

然後安裝你想要的包

Pkg.add("Example")

Julia 的 REPL 擴展性很強,比較有名的比如 OhMyREPL

甚至還可以在 Julia 的 REPL 裡把 C++ 當成動態語言來寫,按 < 鍵進入 C++ 模式(Julia 的 C++ FFI:Cxx.jl,暫時還沒更新到 1.0,需要 0.6)

第三種方式就是在 Atom 這樣支持 cell 的編輯器裡(notebook 也是類似的),在 Atom 中在某一行按 shift+enter 會單獨執行這一行,結果會列印在這一行的後面。如果有多行的結果你可以用滑鼠點擊以下,就會展開。如果你選中了很多行,那麼就會執行你選中的部分,結果顯示在選中的部分最後。

notebook 裡面的使用方法也是 shift + enter 和 Python 等其它語言類似。

下面的部分你可以在以上三種方式裡的任意一種裡執行。

本教程只是幫助熟悉語法,想要掌握 Julia 還請認真閱讀手冊(中文手冊還在翻譯中):

https://docs.julialang.org/en/stable/manual/getting-started/

正如所有的經典教程一樣,我們先來學習怎麼寫 hello world:

在 Julia 裡面寫 hello world 可以這樣寫

> println("Hello World")

注意 在 Julia 裡為了保證聲明可以幫助你區分類型,String 是需要雙引號的,字符使用單引號。

Julia 的字符串繼承了 Perl 的字符串差值,正則表達式等,Stefan 的評價是他覺得 Perl 的字符串處理是最漂亮的,於是就抄了過來。

> name = "Roger"

> println("Hello $name")

這裡 name 是一個變量,Julia 和 Python 一樣,不需要聲明變量,因為所有的變量都只是給值綁定了一個名字而已。然後對於變量插入,可以直接使用 $ 符號。

這將列印出

Hello Roger

當然對於比較長的表達式你也可以使用括號

> println("1 + 1 = $(1 + 1)")

這將列印出

1 + 1 = 2

我們上面提到了怎麼綁定一個變量名:

> x = "Roger"

Julia 的變量名除了 ASCII 字符以外,還可以使用包括但不限於 UTF-8 的 unicode,比如甚至是中文

> 你好 = "Hello!"

還可以是 Emoji,輸入 \:smile 然後再按 tab

> 😄 = "smile"

別忘了這是一個為科學家們打造的語言,還可以利用 LaTeX 來輸入特別的數學符號,在 notebook 或者 REPL 裡輸入 \ + epsilon 按 tab 鍵

> ϵ = 2.2

Julia 還利用了 LLVM 的一些常數(無限精度):

> π

π = 3.1415926535897...

我們寫一個非常簡單的求和函數,它會對一個向量 A 求和

函數聲明使用 function 關鍵字開頭搭配 end 關鍵字,也許一開始你對這個 end 不是很喜歡,或許會問為什麼不像 Python 一樣呢?為什麼不用 {} 呢?別著急後面在元編程的部分告訴你 end 的好處。

然後 for 循環也是一樣的,使用 for 關鍵字,然後可以搭配 in 來遍歷一個數組(是不是幾乎和 Python 一樣?),但是別忘記了所有的代碼塊都最後要寫一個 end

當然 in 關鍵字可以單獨使用,用於判斷某個集合類(collection,例如一個數組)裡面是否包含某個元素

> 1 in [1, 2, 3]

true

注釋方式和 Python 一樣,也使用 #,而多行注釋使用

#=

xxx

=#

但是除此之外,Julia 是有類型的,也可以標註類型(而不是聲明),而對於短小的函數聲明也可以更加的貼近數學語言。例如我們想要判斷某個數字是奇數還是偶數

is_even(x::Int) = x % 2 == 0

Julia 使用 :: 來標註類型 (學過 Python3 的朋友可能知道 Python 也有類似的類型標註但是是:)

這個時候如果輸入了,例如浮點數那麼就會報錯

然後多寫文檔是個好習慣,讓我們給 is_even 和 mysum 加上文檔,對於已經定義過的東西,可以直接這樣加文檔

但是也可以在聲明的時候加

Julia 的文檔系統使用 Documenter.jl ,所有文檔都用 markdown 編寫,這種 markdown 是 Julia flavor 的,具體細則非常簡單還請參見:

https://docs.julialang.org/en/stable/manual/documentation/#Markdown-syntax-1

Julia 裡的分支預測也很簡單,和很多語言都非常像

Julia 也有原生支持的多維數組(而不是 List)甚至有非常完善的 Array Interface。這表現為 Julia 擁有大量的針對不同情況設計的數組類型,例如:可共享數組,供並行計算使用;靜態數組,適合給小的數組加速;稀疏數組,實現上目前只有稀疏矩陣;分布式數組,用於分布式計算;CUDA 數組 CuArray,用於在 N 卡上計算,等等,就不一一列舉了它們之中除了自帶的數組(類似於 numpy 的多維數組)以外都在外部支持的包裡,而所有的這些數組都適用了同樣的 Interface。他們在使用體驗上幾乎沒有差別。

比如可以產生一個隨機數組

> rand(10)

這將得到一個向量,裡面有 10 個元素,每個元素的類型是默認的 Float64 類型。

產生一個隨機矩陣(跟隨你的直覺就好)

> rand(10, 20)

產生一個三維的張量

> rand(10, 20, 30)

...

那麼如果要聲明 Int 類型的數組呢?

那麼如何聲明初始化為 0 的數組呢?現在告訴你函數名稱是 zeros 和 MATLAB 以及 numpy 完全一樣,看了上面 rand 的用法,猜猜這個怎麼用?

那麼如果我需要更複雜的構造呢?Python 的 List 有一個很著名的 List Comprehension 方法,Julia 也有。

> [i for I in 1:10]

這將獲得

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

和 Python 的 List Comprehension 是不是一樣?但是等等,還不止如此,Julia 對多維數組的支持是非常好的,Comprehension 對於多維數組也可以用,用逗號分隔即可

這將得到一個由 Tuple 構成的矩陣,那你已經看到了 Julia 裡面元組使用括號來聲明。而除了這種元組還有一種稱為命名元組(NamedTuple)的東西,這是 1.0 的新特性

info = (name="Roger", age="0", wechat="不告訴你")

你可以直接通過 . 算符來訪問

多維數組的廣播是一個很重要的特性,也是 Julia 多維數組的標準接口(Interface)任何 Julia 的數組都可以使用廣播。Julia 從 MATLAB 學來了 . 算符。任何被點算符作用的函數和其它算符都可以被廣播。例如

> sin.(A)

將廣播 sin 函數到 A 的每一個元素。什麼是廣播簡單來說就是將一個函數作用在多維數組,元組,標量的每一個元素上去。這有點像是函數式編程裡 map 的概念,但是不完全一樣。

廣播運算對任何函數都是支持的,比如

> foo.(A, B, c)

這裡 A 和 B 時兩個數組,c 是一個標量那麼 foo 將會以 foo(a, b, c) 的形式作用在每一個 A,B 的元素 a, b 上。

和很多其它的面向對象語言一樣,Julia 裡所有的東西都是對象,或者說是某個類型的實例,但非 class 的實例,Julia 沒有 class,但是有更輕量級的類型。

定義一個複數類型 複數是很多數值計算和數據分析算法都會用到的類型,然而一般的處理器上並不是自帶複數類型的,我們往往使用兩倍精度的浮點數來模擬一個複數,例如在 C 語言裡,雙精度的複數可以寫作:

double _Complex a;

或者在 C++ 中,雙精度的複數可以寫作:

std::complex<double> a;

在 Python 裡,沒有顯示類型,但是我們可以使用 j:

In [1]: 1 + 1.j

Out[1]: (1+1j)

Julia 裡有自帶的 Complex 類型,和 Python 類似,用 im 表示虛數單位

1 + 1im

Complex 是純 Julia 實現的數值類型。所以我們用這個作為例子來看看怎麼定義一個類型

而實際上和 C/C++ 一樣,Julia 的複數類型也是純 Julia 實現的,我們這裡會簡單地實現一個複數類型 MyComplex。Julia 的類型使用 struct 關鍵字,然後用 end 表示這一段表達式的結束。每個 Julia 類型有一個默認的構造函數,這個構造函數的變量即為類型聲明的成員。

但是僅僅聲明了類型還遠遠不夠,我們還需要對複數定義複數運算,方便起見我們這裡僅重載 * 算符:

(a+b·i)(c+d·i)=(ac-bd)+(ad+bc)·i

首先我們需要將要重載的東西從 Base 模塊中拿出來(而不是自己聲明一個新的,為什麼?留給大家之後思考,這對你理解什麼是 Julia 的多重派發很有幫助),在 Julia 裡,運算符 只是一種特殊的函數,並不會被特別對待

import Base: *

*(a::MyComplex, b::MyComplex) = MyComplex(a.real * b.real - a.imag * b.imag, a.real * b.imag + a.imag * b.real)

然後試試?

b = MyComplex(1.0, 3.0)

a * b

現在輸出不是很好看,我們重載一下 show 方法把默認列印出來的字符串修改一下,這裡字符串裡的 $ 是字符串插入,可以將一個 Julia 表達式的輸出轉換為字符串(String 類型)然後插入到字符串裡。

import Base: show

show(io::IO, ::MIME"text/plain", x::MyComplex) = print(io, "$(x.real) + $(x.imag)im")

我們已經有了一個簡單的複數類型,但是實際上真正在用的時候,我們可能會需要不同精度的複數類型,例如使用 32 位浮點精度的複數類型。為了能夠使用不同的複數類型,我們需要使用參數類型,而複數的實部和虛部都是實數,我們還需要限制其類型範圍

這裡 Real 是自帶的抽象類型,我們之後會講什麼是抽象類型。而 T 則是一個參數。參數類型也有默認的構造函數。

MyComplex2{Float32}(1.0f0, 2.0f0)

但是實際上你還可以定義一些自己的構造函數,在 Julia 裡因為是沒有 class 的,除了構造函數以外的方法都不能寫在類型聲明內部。而一旦你在類型聲明中聲明了一個自己的構造函數,默認的構造將被覆蓋。比如試試下面這個例子

內部構造函數往往是為了在生成這個實例前做一些預處理(例如判斷輸入變量是否符合要求等)更詳細的例子請參見文檔

https://docs.julialang.org/en/latest/manual/constructors/

Julia 語言是沒有 class 的,但這並不意味著 Julia 無法面向對象,Julia 對象的方法(method)通過 多重派發 和 類型樹 進行分配,而不依賴於 class 這種結構。下面這一部分不會影響你使用 Julia 的多重派發特性,因為它非常的直覺化,但是如果你可以通過下面 Python 和 C++ 的例子了解到到底什麼是多重派發和單重派發,那麼也將是非常有益的。

想了解什麼是多重派發,我們要先從單重派發(single dispatch) 說起,大部分支持 class 的語言都是單重派發,我再用 Python 舉個例子,Python 3.4 有一個的提案(PEP 443)裡有一個對單重派發通用函數的提案:

所謂單重派發就是只能按照函數參數的一個類型進行派發方法,從而實現多態。

顧名思義,多重派發就是根據所有參數的類型進行派發方法。C++ 的函數重載其實就是一種靜態的多重派發(static multiple dispatch)。但是到了動態類型就不行了,比如下面這個來自 StackOverflow 的例子

運行上面的 C++ 代碼將會得到

A/A

而我們預期的是

A/B

這是因為 C++ 只支持 Single Dispatch (多重派發在提案中:Report on language support for Multi-Methods and Open-Methods for C++),對於動態類型,編譯器只能通過 class 名稱決定調用的方法,當需要根據參數等信息決定方法的時候就無能為力了。注意,多重派發是一個動態類型的特性,這裡 A,B 都是做成了動態類型,於函數重載不同,一些類似於多重派發在 C++ 中實際上是函數重載,出現歧義(ambiguous)是由於 C++ 的隱式類型轉換。

顧名思義,就是會根據所有的參數來進行派發。例如讓我們在 Julia 裡重新實現 C++ 裡的例子,注意由於 Julia 沒有繼承,我們在這裡用抽象類型代替。Julia 會匹配參數類型最符合的方法,然後調用。在 Julia 裡,由於 Julia 本身是動態語言,函數的重載(overload)與多重派發是一個意思,但是實際上 Julia 的派發會發生在運行時和編譯時,而這在很少的情況下有可能影響性能。

在 Julia 裡,上面的 wooo 稱為一個通用函數(generic function)而對某些類型的具體實現,例如

wooo(a1::TypeA, a2::TypeA) = println("A/A")

稱為 method。

下面來說說 Julia 的類型系統。

Julia 的類型主要分為抽象類型(Abstract Type)和實體類型(Concrete Type),實體類型主要分為可變類型(Mutable Type)和不可變類型(Immutable Type)

抽象類型使用 abstract type 關鍵字 匹配 end 聲明。默認的合成類型都是不可變類型,使用 struct 搭配 end 聲明。而可變類型在 struct 前面增加 mutable 關鍵字即可。某個實體類型(concrete type)是另外一個抽象類型(abstract type)或者抽象類型是另外一個抽象類型的子類,這樣的關係使用 <: 來聲明。

一個抽象類型的所有子類型會構成一顆樹,其中實體類型一定在樹的葉子結點。

下面這個 view_tree 函數會對一顆類型樹進行深度優先遍歷(DFS)

運行會得到 AbstractType 作為父節點的類型樹

再看一個複雜一些的例子(自己運行一下試試):

而 Julia 在分發一個函數的方法的時候,會儘可能匹配最具體的類型,也就是儘可能匹配這顆樹的葉子結點。思考一下下面這段代碼的運行結果

combine(a::AbstractAnimal, b::AbstractAnimal, c::AbstractAnimal) = "three animals get together!" # method 1

combine(a::Sparrow, b::AbstractCat, c::AbstractAnimal) = "a sparrow, a cat and some animal" # method 2

combine(Sparrow(), Kitty(), Sparrow()) # 這個會匹配方法 2

類型在 Julia 裡是非常廉價的,利用多重派發和廉價的類型,我們可以針對數學對象實現更詳細的優化,例如對於滿足不同性質的矩陣,我們有對它們之間互相乘積的優化方法,我們可以將部分操作作為懶惰求值(Lazy Evaluation)加入運算中,然後再為滿足不同性質的矩陣派發精細的優化方法:

實際上 Julia 在標準庫裡已經這麼做了 (雖然實際上依然還有更多的特殊矩陣,你也許可以在 JuliaArrays 裡找到你需要的矩陣類型),不同類型的矩陣會被派發到不同類型的方法上去。

試試對 Julia 自帶的抽象類型 AbstractMatrix 運行這個代碼

view_tree(AbstractMatrix)

Julia 有這樣的特點:廉價的類型和多重派發 + 類型樹的結構,我們可以繼承類型的行為(behavior)而不能繼承類型的成員,而多重派發讓所有 Julia 類型很自然地變成了鴨子類型(Duck Type),我們只要定義好不同的接口 /interface 就足以定義類型的行為。

實際上由於嚴格保持了樹的結構,Julia 也不允許多重繼承,也不存在混入(mixin)這樣的設計模式,這避免了鑽石繼承的問題。

需要說明的是,以上僅僅是 Julia 的特點,它帶來了一些好處,也同時帶來了一些缺點。限於篇幅暫且不表。

問題:想想這幾種 rand 的接口,例如 rand(1:10),rand(Bool), rand(Bool, 2, 2) 等是怎麼實現的?

在當下,如果有人說放棄 Python 那一定是一個很愚蠢的決定,正如開頭所說,Python 和 Julia 各自有其優缺點,而我們在遷移到 Julia 之後依然可以調用我們的一些歷史依賴,並且也依然可以將新寫的更快更簡單的代碼作為 Python 的包提供給 Python 社區。所以你可以選擇

這主要需要依賴於兩個包:PyCall.jl 和 pyjulia。這一部分我們主要講 PyCall.jl

目前 PyCall 還沒有更新到 1.0 但是在 0.6 和 0.7 都是沒有問題的。如果你沒有安裝 PyCall 模塊,請使用 Julia 的包管理器安裝 PyCall,如果你的環境裡沒有安裝 python 或者不在標準路徑中,那麼 Julia 將會下載一個 miniconda 來安裝 python 環境。如果你想使用你已經安裝的 python,請在 Julia 的環境變量 ENV 中設置 python 路徑:

julia> ENV["PYTHON"] = "... python 路徑 ..."

julia> Pkg.build("PyCall")

安裝好之後 PyCall 的使用方法和原生 Python 的語法很接近 (多虧了 Julia 的宏!)

using PyCall

@pyimport numpy as np

np.zeros(10)

Julia 自帶的多維數組類型 Array 和 numpy 可以共享一塊內存,所以當使用 numpy 在 Python 中得到了一個 numpy.ndarray 後在 Julia 裡會看到一個新的 Array。

除了像 @pyimport, @pydef 這樣的宏以外,和其它 FFI(外部函數接口)的模塊一樣,PyCall 也有 python 的字符串字面量,它將會執行一行 python 代碼 / 或者在 main 模塊裡執行一段 Python 代碼,然後將其轉換為 Julia 對象。試試這個

py"sum([1, 2, 3])"

Julia 的元編程(看不懂也沒關係)

作為一個具有 Lisp 血統的語言,元編程是繞不過去的話題。但是 Julia 在宏上的繼承其實是相對克制的。元編程屬於比較深入的內容,這裡我只會簡單地介紹一下,想要深入了解可以看看文檔和 Lazy.jl 這個非常簡單的庫是怎麼實現的,這部分看不懂不會影響你的使用。

在 Julia 裡,所有的東西不僅僅可以是對象,他們也都是表達式,當然表達式也是對象。也許你一開始還在討厭 Julia 的 end 不夠簡潔,但是學了這一部分,你就會發現 end 關鍵字的妙處了。

在 Julia 裡我們可以使用語言本身的語法來處理 Julia 自己的表達式,這被稱為元編程(Meta Programming),那么元編程有什麼用呢?

字符串的字面量是區分不同類型的字符串的一種非常方便的方法,在 Python 裡,我們有正則表達式字面量 r"(.*)",格式化字面量 f"hello {who}"。而在 Julia 裡,則可以通過宏定義自己的字符串字面量,只需聲明以 _str 為結尾的宏即可。

試試這段代碼

這大大方便了需要做大量字符串處理的任務,例如生物信息等。此外這也使得 Julia 很容易在文檔裡支持 LaTeX,Markdown 等多種不同的格式,並且按照統一的接口(Interface)給它們派發不同的方法。

試試自帶的 markdown string literal (markdown 字符串字面量)

在 Julia 裡獲得表達式的方法被稱為引用(quote),可以有兩種方法進行引用

對於短小的表達式使用

進行引用:

ex = :(a + b)

對於大段的表達式,使用 quote 關鍵字進行引用

到了這裡你也許已經發現了,實際上任何一部分 Julia 代碼都是表達式,而不同的表達式有不同的 tag 而正是因為使用了 end 才能夠和各種不同的代碼塊進行匹配。例如實際上函數關鍵字 function 和 end 本身定義了一個代碼塊

所有的表達式都是 Expr,QuoteNode,Symbol 三種類型之一。

當我們有很多個函數嵌套調用的時候會需要打很多括號,現在想要比較方便地用空格表示函數合成例如:g f k l ⇒ g(f(k(l(x)))),我們將在這裡實現一個非常簡單的(單變量)函數合成的宏 @>

使用 @macroexpand 查看你的宏所產生的表達式

@macroexpand @> fa fb fc # => x->fa(fb(fc(x)))

然後想想這個代碼是什麼意思?這裡的 $ 也是插入另外一個表達式的意思

看看是不是成功了!

實際上,作為一個多範式的程式語言,Julia 本身是支持函數式編程的,而函數式編程常常會使用 Lazy 這個包,裡面寫好了更加強大的用於函數合成的宏 @>,它支持多變量函數的合成。


如果你喜歡這篇文章,或希望看到更多類似優質報導,記得給我留言和點讚哦!

相關焦點

  • 一周入門 Julia 靠譜嗎?這有一份免費的學習日程
    Julia 簡明教程 - https://www.shiyanlou.com/courses/1485以下為課程一二節內容:一、Julia 簡介和安裝實驗簡介本課程作為 Julia 這門程式語言的入門教程,旨在介紹其基礎語法,希望大家能夠通過本課程的學習,熟練掌握
  • Julia入門教程-變量
    在Julia語言中,亦是將某個值與之相關聯(或綁定)的名字。它可以用來保存一個值、計算結果等。
  • Julia教程1:Julia學習資料與工作環境
    學習一門語言之前,能夠清楚的知道碰到問題可以去哪裡找答案是非常重要的,同時一個個性化的工作環境更是可以讓工作效率成倍提升。這裡就我收集的一些Julia的學習資料介紹一下,並介紹一下如何搭建一個Julia的工作環境。學習資料由於是一門比較新興的語言,所以其實學習資料並不是特別多,特別是對於那些對計算機編程本身就不熟悉的人來說,更是麻煩。
  • Julia入門教程-算術運算
    <<=向量化 dot運算符Julia 中,每個二元運算符都有一個dot 運算符與之對應,例如^ 就有對應的.^ 存在。比如[1,2,3] ^ 3 是非法的,因為數學上沒有給(長寬不一樣的)數組的立方下過定義。但是[1,2,3] .^ 3 在Julia 裡是合法的,它會逐元素地執行^ 運算(或稱向量化運算),得到[1^3, 2^3, 3^3]。類似地,! 或√ 這樣的一元運算符,也都有一個對應的.√ 用於執行逐元素運算。
  • Julia入門教程-複數和有理數
    Julia 語言包含了預定義的複數和有理數類型,並且支持它們的各種標準數學運算和初等函數。
  • Julia CFD 00 系列說明
    原系列(CFD Python)採用Python作為程序描述語言,基於Python語言的易懂與易學,特別適合於課堂教學。本系列採用Julia語言對原程序代碼進行改寫,利用Julia語言的易學易懂,同時兼具高效計算的特性。
  • Julia CFD|00 系列說明
    原系列(CFD Python)採用Python作為程序描述語言,基於Python語言的易懂與易學,特別適合於課堂教學。本系列採用Julia語言對原程序代碼進行改寫,利用Julia語言的易學易懂,同時兼具高效計算的特性。
  • 1 Julia簡介及安裝
    他們想要的是一個開源的軟體,它要像C語言一般快速而有擁有如同Ruby的動態性;要具有Lisp般真正的同像性而又有Matlab般熟悉的數學記號;要像Python般通用、像R般在統計分析上得心應手、像Perl般自然地處理字符串、像Matlab般具有強大的線性代數運算能力、像shell般膠水語言的能力,易於學習而又不讓真正的黑客感到無聊;還有,它應該是交互式的,同時又是編譯型的……
  • 極簡python教程:快速入門好方法
    其實很久之前,就有身邊的同事或者網友讓我分享一些關於python程式語言的快速教程,他們的痛點同大多數自學程式語言的人一樣,遇到了這些問題:網絡上的信息太多,良莠不全,不知道如何分辨;初學時「冗餘」知識太多,不知道該學些什麼
  • Julia語言與宏觀經濟應用(1): Why Julia?
    當然,我主要是使用Matlab編程,而許坤博士主要是使用R語言。我在2017年初為人師時,讓我的大一學生去自學Python。可是,今天開始,我又推薦大家去學Julia,如果你有「選擇困難症」,我建議你抓鬮,一種即可!我不是為你推薦的,我只是為那些未學任何程式語言、又想學的人提供一個入門的「途徑」。
  • 如何入門線性代數?這裡有一份 Python 線性代數講義
    這個 GitHub 項目介紹了一份入門級線性代數課程講義,適合大學生、程式設計師、數據分析師、算法交易員等,使用的代碼用 Python 語言寫成。項目地址:https://github.com/MacroAnalyst/Linear_Algebra_With_Python1.
  • Julia:集Python、C++、R等語言為一體的全新語言
    2015年7月,該語言的設計者創立了Julia Computing公司,以「開發Julia,它是易於使用,易於部署且易於擴展的產品。」為目標。截至撰寫本文時,該公司有28名員工,客戶從國家實驗室到銀行,從經濟學家到自動駕駛汽車研究人員均包括。
  • 《C 語言入門教程》發布了
    去年發布的《Bash 腳本教程》[2],就是在學習 Shell 的時候寫的。後來學習後端程式語言,深感好多基礎的東西需要補課,就回過頭重新開始看 C 語言。我上一次學習 C 語言,還是在學校裡。印象中,C 語言不容易,複雜類型很難聲明和解讀。
  • Julia語言-「動靜結合」的科學計算語言
    由MIT開發的Julia語言是一種發展迅猛的開源科學計算程式語言。
  • iPhone Swift語言雨燕中文手冊入門教程
    iPhone Swift語言雨燕中文手冊入門教程 來源:www.18183.com作者:集落時間:2014-06-03 6月3日消息,蘋果公司於北京時間今日凌晨1時舉行全球開發者大會
  • 使用Pyhon+Flux+Julia實現手寫數字識別
    使用MNIST數據集對0到9之間的數字進行手寫數字識別是神經網絡的一個典型入門教程
  • 蘋果Apple Swift程式語言中文版入門教程
    蘋果Apple Swift程式語言中文版入門教程 來源:www.18183.com作者:集落時間:2014-06-03 蘋果在WWDC第一天早晨的kyenote中說道全新的開發者程式語言
  • 關於程式語言 Julia,開發團隊有這些想告訴你
    Julia 可以看作是一門集眾家之所長的程式語言,在首次公開時開發團隊就已明確其需求:我們想要一種擁有自由許可的開源語言,同時擁有 C 的速度和 Ruby 的靈活。我們想要一種同像性語言,有像 Lisp 這樣真正的宏,也有像 Matlab 這樣的淺顯熟悉的數學符號。
  • 程式設計師為什麼愛用Julia語言?這裡有五點理由
    Julia是一種免費的現代高級程式語言,於2012年正式發布。作為程式語言大家族中的年輕一員,Julia提供了許多令人眼前一亮的功能和特性。 作為程式設計師,為什麼選擇使用Julia?任何一種程式語言永遠不會是完美的,而且永遠不可能成為一個最終確定的產品,而是會隨著時間的推移不斷變化和發展。本文在此分享程式設計師學習Julia程式語言的五個理由。
  • 如何使用 Julia 語言實現「同態加密+機器學習」?
    而在這些概念背後,少不了一項技術的影子——「同態加密」。本文介紹了使用 Julia 語言進行基於同態加密數據機器學習的全過程,對於入門者具有極大的參考價值。注意:本文討論了最前沿的密碼學技術,旨在提供一種利用「Julia Computing」進行研究的視角。請不要將文中的任何示例用於生產應用程式。在使用密碼學之前一定要諮詢專業的密碼學專家。