Julia教程
Hello World!
使用Julia的解釋器
我們可以新建一個腳本文件hello.jl,在里面寫
print("hello world\n")我們先不要管這行命令具體是怎么回事,你知道它是用來打印Hello World的就可以啦。然后在命令行里用Julia的解釋器執行它
$ julia hello.jl hello world使用REPL環境
或者讓我們打開Julia的REPL
- Linux/Mac:在命令行里輸入julia
- Windows:雙擊Julia的可執行文件或者快捷方式
然后你就進入到了一個交互式的窗口中,輸入這個打印Hello World的命令
julia> print("hello world!\n") hello world!那么這個print函數是干嘛的呢?我們可以在REPL中輸入?+print+enter來獲得它的文檔
julia> help?>print search: print println print_joined print_escaped print_shortest print_unescapedprint(x)Write (to the default output stream) a canonical (un-decorated) textrepresentation of a value if there is one, otherwise call show. Therepresentation used by print includes minimal formatting and tries to avoidJulia-specific details.那么我們就知道了print是說打印輸入的變量的意思
用-e選項執行代碼
安裝好Julia之后,按Ctrl+Shift+T打開命令行(Windows用戶請按Ctrl+X,點擊運行輸入cmd,打開命令行),可以直接運行Julia命令。
$ julia -e 'print("hello world!\n")' hello world!這是Julia的第一種執行命令的方式,-e 選項后面用'括起你要執行的Julia代碼,但注意因為回車會讓命令行執行這行命令所以你必須把所有的代碼寫成一行。一般我們只用這種方式執行一些很短的,很簡單的代碼。
變量
讓我們打開一個REPL,在里面測試這一小節的內容。首先,Julia的變量不需要特別的聲明,變量類型會由你所給它的值自動確定
# 將10賦值給變量x julia> x=10 10 # 對變量x進行運算 julia> x+1 11 # 重新給x賦值 julia> x= 1+1 2 # 但是請不要用數字作為變量名的開頭,因為Julia中默認數字開頭代表了乘法 julia> 2x 4 # 也可以給x賦其它類型的值 julia> x = "hello world!" hello worldJulia的變量名稱支持大部分的Unicode(有極少數的不能支持,但我們不需要管),所以你可以這樣
julia> δ = 0.00001 1e-5 julia> 你好 = "hello" hello這些數學符號可以使用LaTeX語法輸入,中文的話用你自己的輸入法就可以啦,Julia支持了包括UTF-8在內的多種編碼。在REPL中可以這樣輸入一個希臘字母\delta+Tab = δ, 而在編程的時候我們當然不能用命令行,所以有這樣幾個編輯器支持這種補全(如果你發現還有別的好用的編輯器不妨在評論里告訴我):
- Atom+Juno 目前最好用的Julia IDE
- sublime+SublimeLinter-contrib-julialint
Julia有一些自帶的常量,這些常量是允許重載的,但是一般不建議重載它們。比如
julia> pi π = 3.1415926535897... julia> pi = 3 WARNING: imported binding for pi overwritten in module Main 3 julia> π 3整數和浮點數
整數從8位整數支持到128位整數,而浮點數從16位支持到64位浮點數,如果有高精度計算需求還可以使用內建的高精度浮點數BigFloat(256位),整數Int默認跟隨系統位數,比如在32位系統上為32位整數類型Int32,但64位系統就是Int64
Tips:
- 浮點數的精度可以用函數eps求出,比如eps(Float32)
- 比較長的數字可以用_來分割,比如100_101,1000_1010,相當于計數法中的逗號:100,101 1000,1010
復數和分數
在Julia中,虛數單位用im來表示,Julia為虛數對象定義了一些基本的函數
julia> 1+2im 1 + 2im # 實部 julia> real(1+2im) 1 # 虛部 julia> imag(1+2im) 2 # 共軛 julia> conj(1+2im) 1 - 2im # 取模 julia> abs(1+2im) 2.23606797749979 julia> norm(1+2im) 2.23606797749979 # 求幅角 julia> angle(1+2im) 1.1071487177940904運算符
Julia中定義了一些常用的運算符,它們有
數學算符和基本函數
?
Tips:
- Julia中可以使用更加自然的不等式表達方式,比如1<x<2,而不用寫成x>1 and x<2(python) 或者 (x>1)&&(x<2)(類C)
語句
Julia的語句可以默認以一行結尾,比如
x+1 y+2這里x+1和y+1就是分割開的兩條語句,但是我們也可以用;分割語句,比如
x+1;y+1 z+2Julia的代碼塊都以end關鍵字結尾,類似于Pascal,相當于C/C++中的{}聲明代碼塊的方式。也相當于Python中使用空格對齊來分割代碼塊。
類型系統
Julia的類型系統是很有特點的,也常常被認為是Julia的速度快的原因之一。Julia的類型系統不支持類似于Python/Cpp那樣以對象的成員函數和繼承構成的OOP編程范式。Julia中面向對象的編程使用了Multiple Dispatch這一特性,而禁止了類型(對象)中使用成員函數。這種特性繼承自Lisp,稱為OO,而多重派發是實現OO的一個重要手段。
具體關于OO技術的好處可以參看這篇博客:淺談OO編程中必不可少的手段:Multimethod,關于類似于Cpp這種面向對象編程的弊端可以參見這個知乎問題:面向對象編程的弊端是什么
Julia中總共提供了四種類型,它們的關鍵字分別是:type,immutable,abstract,bittype,這里我們暫時只介紹前三種(最后)
復合類型(對象)
在Julia中使用type關鍵字聲明復合類型,以end關鍵字結尾。在type和end之間聲明此類型所包含的變量
type Animalweightsex end當然也可以用分號來分割語句
type Animal weight;sex end當然你也可以什么都不寫,但這種時候建議你最好考慮一下后面介紹到的抽象類型。
type Animal end用type聲明的類型在沒有說明的時候默認以指針賦值,或者說默認使用引用的方式來賦值。除非使用deepcopy函數顯式說明是深度拷貝。
復合類型中的元素可以通過.運算符來訪問
julia> alpaca = Animal("肥的","母的") Animal("肥的","母的") julia> alpaca.sex "母的"指定類型和限定類型
Julia是一個強類型語言,我們可以用::符號來聲明(或者標注)變量類型,比如
julia> (1+2)::AbstractFloat ERROR: TypeError: typeassert: expected AbstractFloat, got Int64julia> (1+2)::Int64 3在復合類型中
type Animalweight::Float64sex::Int end指定類型往往能夠讓Julia程序的運行效率更高。
抽象類型
抽象類型使用abstract關鍵字聲明,聲明抽象類型一般有什么用呢?聲明抽象類型可以很好的幫助你控制接口,具體的例子我們在后面的多重派發介紹。
abstract AbstractAnimal抽象類型可以作為其它類型的父類型出現,比如
abstract Monkey <: AbstractAnimal這里<:運算符代表:左邊是右邊的子類型,它可以在聲明的時候使用,也可以用來做類型判斷
julia> Monkey <: AbstractAnimal true復合類型也可以作為抽象類型的子類型,但不能作為復合類型的子類型。
type Donkey <: AbstractAnimalalive::Bool endTips:
- 那么所有Julia類型的父類型是什么呢?是Any
不可變類型
immutable Complexreal::Float64imag::Float64 end不可變類型是一種類似于type的類型,但是相對type有一些不同:
- 有些時候性能更好這個具體是說當變量所占內存較少的時候,比如像上面的Complex,相對type聲明的類型能夠更有效的打包進數組中,在一些時候編譯器能夠避免為整個immutable類型分配內存。
- immutable中的變量是不能夠被改變的
- 使用immutable的代碼更容易理解
注意,不可變類型中的元素如果是可變類型的,比如數組,那么這個元素依然是可變的
immutable Animalslist_of_animals::Array end這里list_of_animals是可以改變的,但是Animals不能夠被改變
julia> animals = Animals([1,2,3]) Animals([1,2,3]) julia> another_list = [2,3,4] 3-element Array{Int64,1}:234# Animals的元素不能改變 julia> animals.list_of_animals = another_list ERROR: type Animals is immutable# 但是Animals元素的元素可以改變 julia> animals.list_of_animals[2] = 100 100 julia> animals.list_of_animals 3-element Array{Int64,1}:11003產生類型的實例
復合類型type和不可變類型immutable都可以用如下的方法產生實例
type foo1ab endfoo1(1,2)immutable foo2ab endfoo2(2,3)參數輸入的順序是每個類型的元素在類型中的順序
參數類型
Julia的類型也有類似于C++的模板(template)的功能,比如假如現在有Monkey,Donkey,Pig這樣三個類型,都是AbstractAnimal的子類型。但是我想有一個Pet(寵物)類型,我可以養猴子,也能養驢,還能養豬當寵物,所以不能給Pet的成員指定某個類型,但我又不希望它被用來裝別的類型。所以這個時候就需要參數類型。
type Monkey<:AbstractAnimal endtype Donkey<:AbstractAnimal endtype Pig<:AbstractAnimal end type Pet{T<:AbstractAnimal}p::T end這樣在生成這個類型的實例的時候,編譯器就會生成相對應的類型,比如
julia> Pet{Monkey}(Monkey()) Pet{Monkey}(Monkey())注意參數類型在產生實例的時候需要指定參數,編譯器不會自己判斷,當然我們有辦法讓編譯器自己判斷,這個在后面介紹。
Tips
除了類型意外,參數類型的參數還可以是別的東西,比如一個數字
type foo{N} endjulia> foo{2}() foo{2}()函數
在Julia中函數使用function關鍵字來聲明
function add(a,b)return a+b end返回值由return關鍵字指定,或者由最后一個變量指定
function foo()"foo" endjulia> foo() "foo"實際上對于比較短的函數,你還可以這樣聲明
f(x) = x^2julia> f(2) 4構造函數
構造函數是用于定義如何構造對應類型的方法的函數。類似于其它面向對象語言,Julia的復合類型也有構造函數,并且是唯一可以在類型內聲明的函數。構造函數默認是下面這樣的,也就是需要按照內部元素的順序來構造一個新的foo對象。
type foo a::Int b::Float64 endf = foo(1,1.0)實際上它相當于
type foo a::Int b::Float64foo(a::Int, b::Float64) = new(a, b) endJulia的構造函數分為兩種,一種是類型內的構造函數,一種是類型外的構造函數。類型外的構造函數實際上都是調用默認的構造函數創建對象,與一般的函數類似。而類型內的構造函數略有不同,類型內的構造函數可以訪問new函數來為新對象的元素綁定值。new是一個用來給類型元素綁定值的函數,注意這個函數僅有在類型內的構造函數聲明才能夠訪問。
lambda 表達式
lambda表達式可能會是我們很常用的一種函數,它也往往稱為匿名函數,匿名函數的聲明有兩種方式
# 第一種 x->x^2+1 # 第二種 function (x)return x->x^2+1 end第一種聲明方式后面需要跟一整個代碼塊,julia中可以使用begin,end來聲明一段代碼塊(不過這個應該要算在metaprogramming的部分了?)
所以你可以這樣
x->beginif x == "蛤?"return 1elsereturn "excited"endend而很多時候,我們都需要將匿名函數作為某個函數的第一個變量輸入,比如像map,sum這種的。這個時候再寫
map(x->beginif x == "蛤?"return 1elsereturn "excited"endend, [1, "蛤?", 2])未免有些不美觀,影響可讀性,尤其是里面這個匿名函數比較復雜的時候。不用擔心,julia為這種比較長的匿名函數提供了一個可讀性更好的聲明方式
map([1, "蛤?", 2]) do xif x == "蛤?"return 1elsereturn "excited"end enddo關鍵字會產生將后面的代碼塊(到end結束)創建成一個匿名函數然后傳遞給do前面的函數的第一個變量,比如map。有了這個特性,類似于Python的with關鍵字,我們在Julia中操作文件的時候也可以讓它自動關閉
open("outfile", "w") do iowrite(io, data) end循環和判斷
和其它的編程語言類似,尤其是和python類似,julia的for和while也有很多語法糖。
比如
for i = 1:10 @show i end for i in [1,2,3,4,5,6,7,8,9] @show i end等等。然后if,else語句類似于Python,但Python的elif變成了elseif
if x>0println(1) elseif x == 0println(0) elseprintln(3) end同時也支持expr?a:b形式的簡寫,意思是expr為true則執行a,false執行b
模塊和包管理
Julia的名字空間是通過模塊module來進行管理的。
module foo export foo1, foo2 ############ # content ############ end在module和end關鍵字中的代碼塊就會記在這個module下,使用的時候必須先將相應的module所開放的(export)的名字引入,方法有兩種,一種是using,一種是import
using會將后面的模塊中所有開放的變量暴露在當前的空間中,而import會引入相應的模塊名稱。舉個例子,我使用QuDynamics這個模塊的時候,如果
using QuDynamics,那么其中所開放的函數我都可以直接訪問,比如propagate這個函數,我就可以直接使用它。而相應的import QuDynamics,就需要用.運算符訪問模塊的元素,也就是這個模塊所開放的接口,例如QuDynamics.propagate
而Julia的package都是會由一個module進行封裝,在安裝的時候需要知道相應包的名字,比如Plots,然后使用Pkg.add命令進行安裝。例如Pkg.add("Plots")。之后Julia的編譯器就會訪問在METADATA.jl中注冊了的相應包的地址,然后從github上clone相關依賴。當然由于某些原因,這有時候會造成一些用戶出現無法安裝,或者下載緩慢的問題。這在第三版本的包管理器里也許會得到解決。
?
介紹一下IDE的功能:
一圖勝千言,來看看Juno IDE上面有什么吧。
先來看看最左邊的工具條上的那些圖標是什么:
上面3個是代碼文件操作區:
中間4個是功能窗口顯示/關閉相關的
底下3個是運行控制相關的:
先來看看代碼窗口:
當你運行完某一段/某一句代碼,語句的后面會顯示運行的結果:
- 紅色字的就是報錯
- 打勾的表示運行成功
- 有具體內容的按">"號可以展開浮動窗看
- 展開時候浮動窗上面有按鈕(X左邊那個按鈕),可以按下之后擴大來看:
窗口之間有聯動關系:
寫程序的時候,如果忘記了語句的語法,可以實時進行語法查找。下圖就是選擇CSV.read按下鼠標右鍵,選擇Show Documentation
Documentation窗口就會顯示CSV.read的語法。
當然也可以直接在Documentation窗口的搜索框里直接查找語句語法。
如果調用了畫圖等語句,畫出來的圖表就會顯示在Plots窗口。
REPL窗口顯示的就是Julia1.0.0命令行工具:
在這個窗口里,就是和單獨打開命令行窗口一樣操作Julia。
WorkSpace窗口:
示例里WorkSpace窗口和Documentation窗口共用了同一塊顯示區域,單獨點開看看:
WorkSpace窗口顯示的是到目前為止各種語句執行后留在當前內存環境里的各種“對象”(不要問我什么是“對象”,英文叫做"Object"),也就是程序猿經常被調侃的那句經典報錯:“找不到對象”里的那個“對象”。
同樣按下">"號(如果有)可以展開看“對象”的內容,上圖顯示的可展開的兩個數據框(DataFrame)對象。
"Omitted printing of x columns"的意思是有x列數據不展示。也就是說不能看到全部內容,只能看到其中的一部分。
怎么形象的理解IDE:
Juno IDE 就是通過操控Julia命令行工具,擴展出其他的功能窗口,并做了許多方便用戶的關聯操作功能。
好了。今天的內容就到這里。
PS:
目前許多Julia程序包正在做升級維護,以匹配最新版本1.0.0:
以下是本次內容中可以直接安裝在Julia1.0.0的程序包:
using Pkg # 0.7版本之后需要用 Pkg.add 的話需要先調用Pkg包,0.6版本之前不需要.Pkg.add("DataFrames") using DataFramesPkg.add("CSV") using CSV已知ScikitLearn程序包目前不兼容Julia 1.0.0(截至到2018-8-20),目前只兼容到Julia 0.7版本。
但是怪異的是ScikitLearnBase可以兼容1.0.0版本。(筆者還沒去看這兩個包的差異點在哪里)
[注]請大家注意,Pkg.add完之后一定要using一遍,因為Julia的機制是下載歸下載,安裝實際發生在首次調用的時候。
首次調用會進行預編譯(PreCompile),如果在編譯過程中報錯說明這個程序包不適用于這個版本的Julia。
還有一個小小的提示:Julia語言是大小寫敏感(Case Sensitive)的。
如果大家在Github上找到一些牛人的程序包但是沒有放在官方的鏡像里面,可以進行以下操作:
using Pkg Pkg.clone("https://github.com/dmlc/XGBoost.jl.git") Pkg.build("XGBoost")**以上只是操作示例。XGBoost目前也不兼容Julia1.0.0。
總結
- 上一篇: SpringBoot2.0 整合 Spr
- 下一篇: 制作Ubuntu16.04系统盘