當前位置:
首頁 > 知識 > Julia 1.0 正式發布,這是新出爐的一份簡單中文教程

Julia 1.0 正式發布,這是新出爐的一份簡單中文教程

機器之心發布

作者:羅秀哲

兩天前,機器之心報道 MIT 正式發布Julia 1.0 的文章引發了極大的關注(20 多萬的閱讀量),對此編程語言既有批判者也有推崇者。如機器之心了解到 Julia 在科學計算、數據處理行業很受歡迎。為了方便大家更了解這一新正式發布的編程語言,我們推薦 Julia 中文社區(中文社區介紹見文後)組織者之一羅秀哲新出爐的一篇簡單教程。

文章地址:https://zhuanlan.zhihu.com/p/41802723

寫在前面

這兩天的媒體報道可能讓一些人有了恐慌,但是我現在有一個誠懇的建議就是如果你完全沒有編程基礎,時間也不多的話(時間多了不是想學啥學啥),我建議你先學一下 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 這樣的深度學習框架就官方支持了 Julia,這些框架的 Python 用戶轉移過來也並不是什麼難事,但是如果你本來就不擔心自己程序的性能(很多時候這並不是一個大問題),那麼其實不會體會到什麼明顯的不同和優勢。但是這樣說也並不完全正確,Julia 語言的優勢不僅僅在其性能,也在其語言本身的設計。

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

下載和安裝 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://suo.im/4S7gbT

使用什麼編輯器

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 更強大(這件事稍後再說)。

Windows/Mac 用戶:

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

Linux 用戶:

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

樹莓派用戶和超算用戶:

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

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

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

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

2. 如果直接啟動 Julia 會進入到 REPL 里去

你會看到

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

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

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

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

然後安裝你想要的包

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

甚至還可以在 Julia 的 REPL 里把 C++ 當成動態語言來寫,按

3. 第三種方式就是在 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 可以這樣寫

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

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

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

這將列印出

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

這將列印出

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

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

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

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

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

函數聲明

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

函數聲明使用 function 關鍵字開頭搭配 end 關鍵字,所有的 Julia 函數都會返回函數聲明的最後一行,這其實是一種函數式語言的特性。return 關鍵字往往只用於在運行過程中返回。也許一開始你對這個 end 不是很喜歡,或許會問為什麼不像 Python 一樣呢?為什麼不用 {} 呢?別著急後面在元編程的部分告訴你 end 的好處。

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

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

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

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

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。他們在使用體驗上幾乎沒有差別。

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

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

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

產生一個三維的張量

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

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

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

這將獲得

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

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

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

廣播(broadcast)

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

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

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

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

類型——一切都是對象

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

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

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

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

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

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

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

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

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

然後試試?

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

任意精度的複數類型

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

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

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

什麼時候用內部構造函數,什麼時候用外部構造函數?

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

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

多重派發和 Julia 的面向對象

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

想了解什麼是多重派發,我們要先從單重派發(single dispatch)說起,我再用 Python 舉個例子,Python 3.4 有一個的提案(PEP 443)里有一個對單重派發通用函數的提案:

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

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

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

而我們預期的是

這是因為 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)而對某些類型的具體實現,例如

稱為 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 在分發一個函數的方法的時候,會儘可能匹配最具體的類型,也就是儘可能匹配這顆樹的葉子結點。思考一下下面這段代碼的運行結果

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

對滿足 ATA=I 的矩陣,如果遇到了自己的轉置可以什麼都不算

對滿足上三角的矩陣(或者下三角矩陣),在一些矩陣分解等操作的時候可以調用更快的數值方法

而對於單位矩陣,我們總可以什麼都不算

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

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

在 Julia 里調用 Python

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

用 Julia 給 Python 加速

整體遷移到 Julia 上來,但是調用自己的歷史代碼。

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

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

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

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),那麼元編程有什麼用呢?

代碼生成,產生更加高效的代碼(低抽象的代碼)

預處理表達式,提高代碼可讀性(例如實現一個 DSL)

etc.

定義自己的字元串字面量

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

試試這段代碼

這大大方便了需要做大量字元串處理的任務,例如生物信息(這其實是 Julia 的一個重要應用領域,值得一提的是深圳的 Haplox 公司一直在維護 Julia 里用於生物信息處理的 OpenGene.jl) 等。此外這也使得 Julia 很容易在文檔里支持 LaTeX,Markdown 等多種不同的格式,並且按照統一的介面(Interface)給它們派發不同的方法。

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

如何獲得 Julia 的表達式?

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

對於短小的表達式使用 :(blabla) 進行引用

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

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

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

用宏實現函數的合成

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

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

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

看看是不是成功了!

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

好了到此為止你已經掌握了非常基本的 Julia 語法。更進一步還請閱讀官方文檔的手冊部分。希望你可以享受 Julia 的多重派發和元編程!

總結一下

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

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

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

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

有問題去哪裡?

有問題去哪裡問,我首先推薦的是中文和英文的 discourse 論壇,它們的鏈接也都可以在官網找到,地址分別如下:

然後你可以加 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 團隊的成員。

GitHub 地址:JuliaCN

網址:juliacn.org / juliacn.com / juliacn.ac.cn

論壇地址:discourse.juliacn.com

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


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 機器之心 的精彩文章:

恰到好處的機器學習入門課,一站搞定基礎+演算法+實戰
到頭來,這是只有量子計算機才能解決的問題

TAG:機器之心 |