與其他語言的顯著差異
與 MATLAB 的顯著差異
儘管 MATLAB 使用者可能會覺得 Julia 的語法很熟悉,但 Julia 並非 MATLAB 的複製。兩者在語法和功能上有重大的差異。以下是一些值得注意的差異,可能會讓習慣 MATLAB 的 Julia 使用者感到困擾
- Julia 陣列使用方括號編制索引,
A[i,j]
。 - 指派給其他變數時,Julia 陣列不會被複製。在
A = B
之後,變更B
的元素也會修改A
。若要避免這種情況,請使用A = copy(B)
。 - 傳遞給函式的 Julia 值不會被複製。如果函式修改陣列,變更會在呼叫程式中可見。
- Julia 陣列不會在指派陳述式中自動擴充。在 MATLAB 中,
a(4) = 3.2
可以建立陣列a = [0 0 0 3.2]
,而a(5) = 7
可以將其擴充為a = [0 0 0 3.2 7]
,對應的 Julia 陳述式a[5] = 7
會擲回錯誤,如果a
的長度小於 5,或者此陳述式是識別碼a
的第一次使用。Julia 有push!
和append!
,它們能比 MATLAB 的a(end+1) = val
更有效率地擴充Vector
。 - 虛數單位
sqrt(-1)
在 Julia 中表示為im
,而非 MATLAB 中的i
或j
。 - 在 Julia 中,沒有小數點的數字字面值(例如
42
)會建立整數,而非浮點數。因此,如果某些運算預期浮點數,可能會擲回網域錯誤;例如,julia> a = -1; 2^a
會擲回網域錯誤,因為結果不是整數(詳情請參閱 關於網域錯誤的常見問題解答條目)。 - 在 Julia 中,多個值會以元組的形式回傳並指派,例如
(a, b) = (1, 2)
或a, b = 1, 2
。MATLAB 的nargout
,它經常在 MATLAB 中用於根據回傳值的數量來執行選擇性工作,在 Julia 中不存在。相反地,使用者可以使用選擇性參數和關鍵字參數來達成類似的功能。 - Julia 具有真正的單維陣列。列向量的大小為
N
,而不是Nx1
。例如,rand(N)
會建立一個單維陣列。 - 在 Julia 中,
[x,y,z]
將永遠建構一個包含x
、y
和z
的 3 元素陣列。- 要在第一個(「垂直」)維度中串接,請使用
vcat(x,y,z)
或以分號分隔([x; y; z]
)。 - 要在第二個(「水平」)維度中串接,請使用
hcat(x,y,z)
或以空格分隔([x y z]
)。 - 要建構區塊矩陣(在頭兩個維度中串接),請使用
hvcat
或結合空格和分號([a b; c d]
)。
- 要在第一個(「垂直」)維度中串接,請使用
- 在 Julia 中,
a:b
和a:b:c
建構AbstractRange
物件。若要建構一個像 MATLAB 中的完整向量,請使用collect(a:b)
。不過,通常不需要呼叫collect
。在大部分情況下,AbstractRange
物件會像一般陣列一樣運作,但它會延遲計算其值,因此更有效率。這種建立特殊物件而非完整陣列的模式經常使用,也見於函式中,例如range
,或迭代器中,例如enumerate
和zip
。這些特殊物件大多可以用作一般陣列。 - Julia 中的函式會從其最後一個表達式或
return
關鍵字傳回值,而不是在函式定義中列出要傳回的變數名稱(詳細資訊請參閱 The return Keyword)。 - Julia 程式碼可以包含任意數量的函式,且當載入檔案時,所有定義都會對外可見。函式定義可以從目前工作目錄以外的檔案載入。
- 在 Julia 中,當使用單一引數呼叫時,會對陣列的每個元素執行簡約運算,例如
sum
、prod
和max
,例如sum(A)
,即使A
有多個維度。 - 在 Julia 中,必須使用括弧來呼叫沒有引數的函式,例如
rand()
。 - Julia 不建議使用分號來結束陳述式。陳述式的結果不會自動列印(互動式提示字元除外),且程式碼列不需要以分號結尾。可以透過
println
或@printf
來列印特定輸出。 - 在 Julia 中,如果
A
和B
是陣列,邏輯比較運算式(例如A == B
)不會傳回布林陣列。請改用A .== B
,其他布林運算子(例如<
、>
)也一樣。 - 在 Julia 中,運算子
&
、|
和⊻
(xor
) 分別執行等同於 MATLAB 中and
、or
和xor
的位元運算,優先順序類似於 Python 的位元運算子(與 C 不同)。它們可以在純量或陣列中逐元素運算,也可以用來組合邏輯陣列,但請注意運算順序的差異:可能需要括號(例如,要選取等於 1 或 2 的A
元素,請使用(A .== 1) .| (A .== 2)
)。 - 在 Julia 中,集合的元素可以使用散佈運算子
...
作為引數傳遞給函式,例如xs=[1,2]; f(xs...)
。 - Julia 的
svd
會傳回奇異值向量,而不是稠密對角矩陣。 - 在 Julia 中,
...
不用於繼續程式碼行。相反地,未完成的表達式會自動繼續到下一行。 - 在 Julia 和 MATLAB 中,變數
ans
會設定為互動式工作階段中發出的最後一個表達式的值。與 MATLAB 不同,在 Julia 中,當 Julia 程式碼以非互動模式執行時,不會設定ans
。 - 與 MATLAB 的
class
不同,Julia 的struct
不支援在執行階段動態新增欄位。請改用Dict
。Julia 中的 Dict 沒有順序。 - 在 Julia 中,每個模組都有自己的全域範圍/名稱空間,而在 MATLAB 中只有一個全域範圍。
- 在 MATLAB 中,移除不需要值的慣用語法是使用邏輯索引,例如在表達式
x(x>3)
中或在陳述式x(x>3) = []
中來修改x
。相較之下,Julia 提供高階函式filter
和filter!
,讓使用者可以撰寫filter(z->z>3, x)
和filter!(z->z>3, x)
作為對應的轉譯x[x.>3]
和x = x[x.>3]
的替代方案。使用filter!
可以減少暫時陣列的使用。 - 擷取(或「解除參考」)儲存格陣列所有元素的類比,例如在 MATLAB 中的
vertcat(A{:})
,在 Julia 中使用散佈運算子撰寫,例如vcat(A...)
。 - 在 Julia 中,
adjoint
函式執行共軛轉置;在 MATLAB 中,adjoint
提供「伴隨矩陣」或經典伴隨矩陣,也就是餘因子矩陣的轉置。 - 在 Julia 中,a^b^c 會評估為 a^(b^c),而在 MATLAB 中會評估為 (a^b)^c。
與 R 的顯著差異
Julia 的目標之一是提供一種有效的語言來進行資料分析和統計程式設計。對於從 R 轉換到 Julia 的使用者,以下是一些顯著的差異
Julia 的單引號括住字元,而不是字串。
Julia 可以透過索引字串來建立子字串。在 R 中,必須先將字串轉換為字元向量才能建立子字串。
在 Julia 中,就像 Python 但不像 R,字串可以使用三引號
""" ... """
建立。此語法對於建立包含換行符號的字串很方便。在 Julia 中,變數長度參數使用擴散運算子
...
指定,它總是跟在特定變數名稱之後,這與 R 不同,後者中...
可以單獨出現。在 Julia 中,模數是
mod(a, b)
,而不是a %% b
。Julia 中的%
是取餘運算子。Julia 使用方括號建立向量。Julia 的
[1, 2, 3]
等同於 R 的c(1, 2, 3)
。在 Julia 中,並非所有資料結構都支援邏輯索引。此外,Julia 中的邏輯索引僅支援長度等於被索引物件的向量。例如
- 在 R 中,
c(1, 2, 3, 4)[c(TRUE, FALSE)]
等同於c(1, 3)
。 - 在 R 中,
c(1, 2, 3, 4)[c(TRUE, FALSE, TRUE, FALSE)]
等同於c(1, 3)
。 - 在 Julia 中,
[1, 2, 3, 4][[true, false]]
會擲出BoundsError
。 - 在 Julia 中,
[1, 2, 3, 4][[true, false, true, false]]
會產生[1, 3]
。
- 在 R 中,
與許多語言一樣,Julia 不總是允許對不同長度的向量進行運算,這與 R 不同,後者中向量只需要共用一個共同的索引範圍即可。例如,
c(1, 2, 3, 4) + c(1, 2)
在 R 中是有效的,但等效的[1, 2, 3, 4] + [1, 2]
會在 Julia 中擲出錯誤。當逗號不改變程式碼的意義時,Julia 允許使用可選的尾隨逗號。這可能會讓 R 使用者在索引陣列時感到困惑。例如,R 中的
x[1,]
會傳回矩陣的第一列;然而,在 Julia 中,逗號會被忽略,因此x[1,] == x[1]
,並且會傳回第一個元素。若要擷取列,請務必使用:
,例如x[1,:]
。Julia 的
map
先取函式,然後再取其引數,這與 R 中的lapply(<structure>, function, ...)
不同。類似地,Julia 中等效於 R 中apply(X, MARGIN, FUN, ...)
的是mapslices
,其中函式是第一個引數。R 中的多變數套用,例如
mapply(choose, 11:13, 1:3)
,可以在 Julia 中寫成broadcast(binomial, 11:13, 1:3)
。同樣地,Julia 提供了更簡短的點語法來向量化函式binomial.(11:13, 1:3)
。Julia 使用
end
來表示條件區塊的結尾,例如if
,迴圈區塊,例如while
/for
,以及函式。Julia 允許if cond; statement; end
、cond && statement
和!cond || statement
等形式的陳述式,取代單行的if ( cond ) statement
。後兩者語法中的賦值陳述式必須明確地用括號包起來,例如cond && (x = value)
。在 Julia 中,
<-
、<<-
和->
不是賦值運算子。Julia 的
->
會建立一個匿名函式。Julia 的
*
運算子可以執行矩陣乘法,這與 R 不同。如果A
和B
是矩陣,則A * B
表示 Julia 中的矩陣乘法,等於 R 中的A %*% B
。在 R 中,此相同符號會執行元素乘法(Hadamard 乘法)。若要取得元素乘法運算,您需要在 Julia 中寫成A .* B
。Julia 使用
transpose
函式執行矩陣轉置,並使用'
運算子或adjoint
函式執行共軛轉置。因此,Julia 的transpose(A)
等於 R 的t(A)
。此外,Julia 中的非遞迴轉置是由permutedims
函式提供的。Julia 在撰寫
if
陳述式或for
/while
迴圈時不需要括號:使用for i in [1, 2, 3]
取代for (i in c(1, 2, 3))
,並使用if i == 1
取代if (i == 1)
。Julia 沒有將數字
0
和1
視為布林值。您不能在 Julia 中寫入if (1)
,因為if
陳述式只接受布林值。相反地,您可以寫入if true
、if Bool(1)
或if 1==1
。Julia 不提供
nrow
和ncol
。請改用size(M, 1)
取代nrow(M)
,並用size(M, 2)
取代ncol(M)
。Julia 仔細區分純量、向量和矩陣。在 R 中,
1
和c(1)
是相同的。在 Julia 中,它們不能互換使用。Julia 無法將函式呼叫的結果指定給指定作業運算的左手邊:您無法撰寫
diag(M) = fill(1, n)
。Julia 不鼓勵在主命名空間中填入函式。Julia 的大多數統計功能都可以在 套件 中找到,這些套件位於 JuliaStats 組織 中。例如
- 機率分佈相關的函式由 Distributions 套件 提供。
- 資料框由 DataFrames 套件 提供。
- 廣義線性模型由 GLM 套件 提供。
Julia 提供元組和真正的雜湊表,但不提供 R 風格的清單。傳回多個項目時,您通常應該使用元組或命名元組:請改用
(1, 2)
或(a=1, b=2)
,不要使用list(a = 1, b = 2)
。Julia 鼓勵使用者撰寫自己的類型,這些類型比 R 中的 S3 或 S4 物件更容易使用。Julia 的多重分派系統表示
table(x::TypeA)
和table(x::TypeB)
會像 R 的table.TypeA(x)
和table.TypeB(x)
一樣作用。在 Julia 中,指派或傳遞給函式時不會複製值。如果函式修改陣列,呼叫者將可以看到變更。這與 R 非常不同,而且讓新函式能夠更有效率地處理大型資料結構。
在 Julia 中,使用
hcat
、vcat
和hvcat
串聯向量和矩陣,而不是像在 R 中使用c
、rbind
和cbind
。在 Julia 中,像
a:b
這樣的範圍並非像在 R 中那樣是向量的簡寫,而是一個用於迭代的特殊AbstractRange
物件。若要將範圍轉換為向量,請使用collect(a:b)
。:
算子在 R 和 Julia 中具有不同的優先順序。特別是,在 Julia 中,算術運算子的優先順序高於:
算子,而在 R 中則相反。例如,Julia 中的1:n-1
等於 R 中的1:(n-1)
。Julia 中的
max
和min
分別等於 R 中的pmax
和pmin
,但兩個參數必須具有相同的維度。雖然maximum
和minimum
在 R 中取代了max
和min
,但它們之間存在重要的差異。Julia 的
sum
、prod
、maximum
和minimum
與 R 中的同類函數不同。它們都接受一個可選的關鍵字參數dims
,用於指示執行運算的維度。例如,在 Julia 中A = [1 2; 3 4]
,在 R 中B <- rbind(c(1,2),c(3,4))
是同一個矩陣。那麼sum(A)
給出的結果與sum(B)
相同,但sum(A, dims=1)
是包含每一列總和的列向量,而sum(A, dims=2)
是包含每一行總和的列向量。這與 R 的行為形成對比,在 R 中,單獨的colSums(B)
和rowSums(B)
函數提供了這些功能。如果dims
關鍵字參數是一個向量,那麼它指定執行總和的所有維度,同時保留求和數組的維度,例如sum(A, dims=(1,2)) == hcat(10)
。需要注意的是,對於第二個參數沒有錯誤檢查。在 R 中,效能需要向量化。在 Julia 中,幾乎相反:效能最佳的代碼通常是通過使用非向量化迴圈來實現的。
Julia 是熱切求值的,不支持 R 風格的惰性求值。對於大多數使用者來說,這意味著幾乎沒有未加引號的表達式或欄位名稱。
Julia 不支援
NULL
類型。最接近的等價物是nothing
,但它的行為更像一個標量值,而不是一個列表。使用x === nothing
而不是is.null(x)
。在 Julia 中,遺失值由
missing
物件表示,而不是由NA
表示。使用ismissing(x)
(或ismissing.(x)
對於向量的元素級運算)而不是is.na(x)
。通常使用skipmissing
函數代替na.rm=TRUE
(儘管在某些特定情況下,函數會採用skipmissing
參數)。Julia 沒有 R 的
assign
或get
等效項。在 Julia 中,
return
不需要括號。在 R 中,移除不需要值的慣用語法是使用邏輯索引,例如表達式
x[x>3]
或陳述式x = x[x>3]
來修改x
。相反地,Julia 提供高階函式filter
和filter!
,讓使用者可以撰寫filter(z->z>3, x)
和filter!(z->z>3, x)
作為對應的轉寫x[x.>3]
和x = x[x.>3]
的替代方案。使用filter!
可以減少暫時陣列的使用。
與 Python 的顯著差異
- Julia 的
for
、if
、while
等區塊以end
關鍵字終止。縮排層級不像 Python 中那樣有意義。與 Python 不同的是,Julia 沒有pass
關鍵字。 - 字串在 Julia 中以雙引號表示(
"text"
)(多行字串使用三個雙引號),而在 Python 中則可以用單引號('text'
)或雙引號("text"
)表示。Julia 中的字元使用單引號('c'
)。 - 字串串接在 Julia 中使用
*
,而不是像 Python 中的+
。類似的,字串重複使用^
,而不是*
。Julia 中不會像 Python 中那樣隱式串接字串文字(例如'ab' 'cd' == 'abcd'
)。 - Python 清單(靈活但速度慢)對應到 Julia 的
Vector{Any}
型別,或更通用的Vector{T}
,其中T
是某個非具體元素型別。像 NumPy 陣列那樣儲存元素在原地的「快速」陣列(即dtype
為np.float64
、[('f1', np.uint64), ('f2', np.int32)]
等)可以用Array{T}
表示,其中T
是具體的、不可變的元素型別。這包括內建型別,例如Float64
、Int32
、Int64
,但也包括更複雜的型別,例如Tuple{UInt64,Float64}
以及許多使用者定義的型別。 - 在 Julia 中,陣列、字串等的索引是從 1 開始,而不是從 0 開始。
- 與 Python 不同,Julia 的切片索引包含最後一個元素。Julia 中的
a[2:3]
在 Python 中是a[1:3]
。 - 與 Python 不同,Julia 允許 具有任意索引的 AbstractArrays。Python 對負索引的特殊解釋,
a[-1]
和a[-2]
,在 Julia 中應寫成a[end]
和a[end-1]
。 - Julia 需要
end
來索引直到最後一個元素。Python 中的x[1:]
等於 Julia 中的x[2:end]
。 - 在 Julia 中,任何物件前面的
:
會建立一個Symbol
或引用一個表達式;因此,x[:5]
與x[5]
相同。如果您想取得陣列的前n
個元素,請使用範圍索引。 - Julia 的範圍索引格式為
x[start:step:stop]
,而 Python 的格式為x[start:(stop+1):step]
。因此,Python 中的x[0:10:2]
等於 Julia 中的x[1:2:10]
。類似地,Python 中的x[::-1]
,指的是反轉的陣列,等於 Julia 中的x[end:-1:1]
。 - 在 Julia 中,範圍可以獨立建構為
start:step:stop
,它在陣列索引中使用相同的語法。range
函數也受支援。 - 在 Julia 中,使用陣列索引矩陣,例如
X[[1,2], [1,3]]
,指的是包含第一和第二列與第一和第三列交集的子矩陣。在 Python 中,X[[1,2], [1,3]]
指的是一個向量,其中包含矩陣中單元格[1,1]
和[2,3]
的值。Julia 中的X[[1,2], [1,3]]
等於 Python 中的X[np.ix_([0,1],[0,2])]
。Python 中的X[[0,1], [0,2]]
等於 Julia 中的X[[CartesianIndex(1,1), CartesianIndex(2,3)]]
。 - Julia 沒有換行語法:如果在行的結尾,目前的輸入是一個完整的表達式,則視為完成;否則輸入會繼續。強制表達式繼續的一種方法是用括號將其包起來。
- Julia 陣列是欄優先(Fortran 順序),而 NumPy 陣列預設是列優先(C 順序)。若要在陣列上執行迴圈時獲得最佳效能,應在 Julia 中反轉迴圈順序(請參閱效能提示中的相關部分)。
- Julia 的更新運算子(例如
+=
、-=
等)不是原地運算,而 NumPy 的更新運算子是。這表示A = [1, 1]; B = A; B += [3, 3]
並不會變更A
中的值,而是將名稱B
重新繫結到右手邊B = B + 3
的結果,也就是一個新的陣列。若要執行原地運算,請使用B .+= 3
(請參閱點運算子)、明確迴圈或InplaceOps.jl
。 - 與 Python 中只在定義函式時評估預設值的作法不同,Julia 會在每次呼叫方法時評估函式引數的預設值。例如,函式
f(x=rand()) = x
會在每次呼叫時沒有引數時傳回一個新的亂數。另一方面,函式g(x=[1,2]) = push!(x,3)
會在每次呼叫為g()
時傳回[1,2,3]
。 - 在 Julia 中,關鍵字引數必須使用關鍵字傳遞,與 Python 中通常可以用位置傳遞關鍵字引數不同。嘗試以位置傳遞關鍵字引數會變更方法簽章,導致
MethodError
或呼叫錯誤的方法。 - 在 Julia 中,
%
是餘數運算子,而在 Python 中是模數運算子。 - 在 Julia 中,常用的
Int
類型對應到機器整數類型 (Int32
或Int64
),這與 Python 中int
是任意長度整數不同。這表示在 Julia 中,Int
類型會溢位,因此2^64 == 0
。如果您需要較大的值,請使用其他適當的類型,例如Int128
、BigInt
或浮點類型,例如Float64
。 - 虛數單位
sqrt(-1)
在 Julia 中表示為im
,而不是 Python 中的j
。 - 在 Julia 中,指數運算子是
^
,而不是 Python 中的**
。 - Julia 使用類型為
Nothing
的nothing
來表示空值,而 Python 使用類型為NoneType
的None
。 - 在 Julia 中,矩陣類型上的標準運算子是矩陣運算,而在 Python 中,標準運算子是逐元素運算。當
A
和B
都是矩陣時,Julia 中的A * B
執行矩陣乘法,而不是 Python 中的逐元素乘法。Julia 中的A * B
等於 Python 中的A @ B
,而 Python 中的A * B
等於 Julia 中的A .* B
。 - Julia 中的共軛運算子
'
會傳回向量的共軛 (列向量的延遲表示法),而 Python 中向量的轉置運算子.T
會傳回原始向量 (非運算)。 - 在 Julia 中,函式可能包含多個具體實作 (稱為「方法」),這些實作會透過多重分派根據呼叫中所有引數的類型來選擇,這與 Python 中的函式不同,Python 中的函式只有一個實作,而且沒有多型性 (與使用不同語法並允許對方法接收者進行分派的 Python 方法呼叫相反)。
- Julia 中沒有類別。取而代之的是結構 (可變或不可變),其中包含資料但沒有方法。
- 在 Python 中呼叫類別實例的方法 (
x = MyClass(*args); x.f(y)
) 對應到 Julia 中的函式呼叫,例如x = MyType(args...); f(x, y)
。一般來說,多重分派比 Python 類別系統更靈活且更強大。 - Julia 結構可能只有一個抽象的超類型,而 Python 類別可以繼承一個或多個(抽象或具體)的超類別。
- Julia 的邏輯程式結構(套件和模組)與檔案結構(包含其他檔案的
include
)無關,而 Python 程式碼結構則由目錄(套件)和檔案(模組)定義。 - Julia 中的三元運算子
x > 0 ? 1 : -1
對應於 Python 中的條件式1 if x > 0 else -1
。 - 在 Julia 中,
@
符號表示巨集,而在 Python 中則表示裝飾器。 - Julia 中的例外處理使用
try
—catch
—finally
,而不是try
—except
—finally
。與 Python 相反,不建議在 Julia 的正常工作流程中使用例外處理(與 Python 相比,Julia 在一般控制流程中較快,但在例外捕捉中較慢)。 - Julia 中的迴圈很快,不需要為了效能而撰寫「向量化」程式碼。
- 小心 Julia 中的非常數全域變數,特別是在緊密迴圈中。由於可以在 Julia 中撰寫接近金屬的程式碼(與 Python 不同),因此全域變數的影響可能會很劇烈(請參閱 效能提示)。
- 在 Julia 中,捨入和截斷是明確的。Python 的
int(3.7)
應該是floor(Int, 3.7)
或Int(floor(3.7))
,並且與round(Int, 3.7)
不同。floor(x)
和round(x)
本身會傳回與x
相同類型的整數值,而不是總是傳回Int
。 - 在 Julia 中,解析是明確的。Python 的
float("3.7")
在 Julia 中會是parse(Float64, "3.7")
。 - 在 Python 中,大部分值都可以在邏輯語境中使用(例如:
if "a":
表示執行後面的區塊,而if "":
表示不執行)。在 Julia 中,你需要明確轉換為Bool
(例如:if "a"
會擲出例外)。如果你想在 Julia 中測試非空字串,你可以明確寫成if !isempty("")
。或許令人驚訝的是,在 Python 中if "False"
和bool("False")
都會評估為True
(因為"False"
是非空字串);在 Julia 中,parse(Bool, "false")
會傳回false
。 - 在 Julia 中,大部分程式碼區塊都會引入新的區域範圍,包括迴圈和
try
—catch
—finally
。請注意,在 Python 和 Julia 中,解析(清單、產生器等)都會引入新的區域範圍,而if
區塊在兩種語言中都不會引入新的區域範圍。
值得注意的與 C/C++ 的差異
- Julia 陣列使用方括號索引,並且可以有多個維度
A[i,j]
。此語法不僅僅是 C/C++ 中對指標或位址參照的語法糖。請參閱 關於陣列建構的手冊條目。 - 在 Julia 中,陣列、字串等的索引是從 1 開始,而不是從 0 開始。
- Julia 陣列在指定給另一個變數時不會複製。在
A = B
之後,變更B
的元素也會修改A
。更新運算子(例如+=
)不會就地運算,它們等於A = A + B
,這會將左側重新繫結到右側表達式的結果。 - Julia 陣列是欄優先(Fortran 順序),而 C/C++ 陣列預設是列優先。要在迴圈陣列時獲得最佳效能,應在 Julia 中反轉迴圈順序(請參閱 效能秘訣中的相關章節)。
- Julia 值在指派或傳遞至函數時不會被複製。如果函數修改陣列,變更將會在呼叫者中可見。
- 在 Julia 中,空白字元具有意義,這與 C/C++ 不同,因此在 Julia 程式中新增/移除空白字元時必須小心。
- In Julia, literal numbers without a decimal point (such as
42
) create signed integers, of typeInt
, but literals too large to fit in the machine word size will automatically be promoted to a larger size type, such asInt64
(ifInt
isInt32
),Int128
, or the arbitrarily largeBigInt
type. There are no numeric literal suffixes, such asL
,LL
,U
,UL
,ULL
to indicate unsigned and/or signed vs. unsigned. Decimal literals are always signed, and hexadecimal literals (which start with0x
like C/C++), are unsigned, unless when they encode more than 128 bits, in which case they are of typeBigInt
. Hexadecimal literals also, unlike C/C++/Java and unlike decimal literals in Julia, have a type based on the length of the literal, including leading 0s. For example,0x0
and0x00
have typeUInt8
,0x000
and0x0000
have typeUInt16
, then literals with 5 to 8 hex digits have typeUInt32
, 9 to 16 hex digits typeUInt64
, 17 to 32 hex digits typeUInt128
, and more that 32 hex digits typeBigInt
. This needs to be taken into account when defining hexadecimal masks, for example~0xf == 0xf0
is very different from~0x000f == 0xfff0
. 64 bitFloat64
and 32 bitFloat32
bit literals are expressed as1.0
and1.0f0
respectively. Floating point literals are rounded (and not promoted to theBigFloat
type) if they can not be exactly represented. Floating point literals are closer in behavior to C/C++. Octal (prefixed with0o
) and binary (prefixed with0b
) literals are also treated as unsigned (orBigInt
for more than 128 bits). - 在 Julia 中,除法運算子
/
在兩個運算元都是整數型別時,會傳回浮點數。若要執行整數除法,請使用div
或÷
。 - 在 Julia 中,使用浮點型別索引
Array
通常會產生錯誤。C 語句a[i / 2]
在 Julia 中的等效語法為a[i ÷ 2 + 1]
,其中i
為整數型別。 - 字串文字可以用
"
或"""
分隔,"""
分隔的文字可以包含"
字元,而不用加上引號,例如"\""
。字串文字可以插入其他變數或運算式的值,用$變數名稱
或$(運算式)
表示,這會在函數的內容中評估變數名稱或運算式。 //
表示Rational
數字,而不是單行註解(在 Julia 中為#
)#=
表示多行註解的開頭,=#
表示結尾。- Julia 中的函數會從最後一個運算式或
return
關鍵字傳回值。函數可以傳回多個值,並指定為組,例如(a, b) = myfunction()
或a, b = myfunction()
,而不是像在 C/C++ 中必須傳遞指標至值(例如a = myfunction(&b)
)。 - Julia 不需要使用分號來結束陳述式。表達式的結果並不會自動列印(互動式提示字元除外,也就是 REPL),而且程式碼行也不需要以分號結尾。
println
或@printf
可用於列印特定輸出。在 REPL 中,;
可用於抑制輸出。;
在[ ]
中也有不同的意思,這是需要注意的地方。;
可用於分隔單一行上的表達式,但在許多情況下並非絕對必要,而且更像是可讀性的輔助工具。 - 在 Julia 中,運算子
⊻
(xor
) 執行位元 XOR 運算,也就是 C/C++ 中的^
。此外,位元運算子的優先順序與 C/C++ 不同,因此可能需要使用括號。 - Julia 的
^
是指數運算(pow),而不是像 C/C++ 中的位元 XOR(在 Julia 中請使用⊻
或xor
) - Julia 有兩個右位移運算子,
>>
和>>>
。>>
執行算術位移,>>>
始終執行邏輯位移,這與 C/C++ 不同,在 C/C++ 中>>
的意義取決於被位移值的類型。 - Julia 的
->
會建立一個匿名函式,它並不會透過指標存取成員。 - Julia 在撰寫
if
陳述式或for
/while
迴圈時不需要括號:使用for i in [1, 2, 3]
取代for (int i=1; i <= 3; i++)
,使用if i == 1
取代if (i == 1)
。 - Julia 沒有將數字
0
和1
視為布林值。您不能在 Julia 中寫入if (1)
,因為if
陳述式只接受布林值。相反地,您可以寫入if true
、if Bool(1)
或if 1==1
。 - Julia 使用
end
表示條件區塊的結尾,例如if
,迴圈區塊,例如while
/for
,以及函式。Julia 允許使用if cond; statement; end
、cond && statement
和!cond || statement
等形式的陳述式,以取代單行if ( cond ) statement
。後兩者語法的賦值陳述式必須明確地用括號包起來,例如cond && (x = value)
,這是因為運算子優先順序的關係。 - Julia 沒有換行語法:如果在行的結尾,目前的輸入是一個完整的表達式,則視為完成;否則輸入會繼續。強制表達式繼續的一種方法是用括號將其包起來。
- Julia 巨集會對已解析的表達式進行運算,而不是程式碼文字,這讓它們可以對 Julia 程式碼執行複雜的轉換。巨集名稱以
@
字元開頭,並同時具備函式類型的語法@mymacro(arg1, arg2, arg3)
和陳述式類型的語法@mymacro arg1 arg2 arg3
。這些形式可以互換;函式類型的形式特別適用於巨集出現在另一個表達式中時,而且通常最為清楚。陳述式類型的形式通常用於註解區塊,例如在分散式for
結構中:@distributed for i in 1:n; #= body =#; end
。在巨集結構的結尾可能不明確時,請使用函式類型的形式。 - Julia 有列舉類型,使用巨集
@enum(name, value1, value2, ...)
表示。例如:@enum(Fruit, banana=1, apple, pear)
- 依慣例,修改其參數的函數名稱末端會加上
!
,例如push!
。 - 在 C++ 中,預設為靜態調度,也就是說,你需要將函數註解為虛擬函數,才能進行動態調度。另一方面,在 Julia 中,每個方法都是「虛擬的」(儘管它比這更廣義,因為方法會針對每個參數類型進行調度,而不僅是
this
,使用最具體宣告規則)。
Julia ⇔ C/C++:命名空間
- C/C++
namespace
大致對應到 Juliamodule
。 - Julia 中沒有私有全域變數或欄位。所有內容都可以透過完全限定路徑(或相對路徑,如果需要)公開存取。
using MyNamespace::myfun
(C++)大致對應到import MyModule: myfun
(Julia)。using namespace MyNamespace
(C++)大致對應到using MyModule
(Julia)- 在 Julia 中,只有
export
的符號會提供給呼叫模組。 - 在 C++ 中,只有包含在包含(公開)標頭檔中的元素會提供出來。
- 在 Julia 中,只有
- 注意事項:
import
/using
關鍵字(Julia)也會載入模組(見下文)。 - 注意事項:
import
/using
(Julia)僅在全域範圍層級(module
)中運作- 在 C++ 中,
using namespace X
在任意範圍內運作(例如:函數範圍)。
- 在 C++ 中,
Julia ⇔ C/C++:模組載入
- 當你想到 C/C++「函式庫」時,你可能會尋找 Julia「套件」。
- 注意事項:C/C++ 函式庫通常包含多個「軟體模組」,而 Julia「套件」通常只包含一個。
- 提醒:Julia
module
是全域範圍(不一定是「軟體模組」)。
- 取代建置/
make
腳本,Julia 使用「專案環境」(有時稱為「專案」或「環境」)。- 建置腳本僅供較複雜的應用程式(例如需要編譯或下載 C/C++ 可執行檔的應用程式)使用。
- 要在 Julia 中開發應用程式或專案,您可以將其根目錄初始化為「專案環境」,並將應用程式特定的程式碼/套件置於其中。這能有效控制專案相依性,並利於日後重現。
- 可用的套件會透過
Pkg.add()
函數或 Pkg REPL 模式加入「專案環境」。(不過,這不會載入所述套件)。 - 「專案環境」中可用的套件清單(直接相依性)會儲存在其
Project.toml
檔案中。 - 「專案環境」的完整相依性資訊會由
Pkg.resolve()
自動產生並儲存在其Manifest.toml
檔案中。
- 可供「專案環境」使用的套件(「軟體模組」)會透過
import
或using
載入。- 在 C/C++ 中,您會使用
#include <moduleheader>
來取得物件/函數宣告,並在建置可執行檔時連結至函式庫。 - 在 Julia 中,再次呼叫 using/import 僅會將現有模組納入範圍,但不會再次載入(類似於在 C/C++ 中加入非標準的
#pragma once
)。
- 在 C/C++ 中,您會使用
- 基於目錄的套件存放庫(Julia)可以透過將存放庫路徑加入
Base.LOAD_PATH
陣列來提供。- 來自基於目錄的存放庫的套件在透過
import
或using
載入之前不需要Pkg.add()
工具。它們只會提供給專案使用。 - 基於目錄的套件存放庫是開發「軟體模組」的最快解決方案。
- 來自基於目錄的存放庫的套件在透過
Julia ⇔ C/C++:組裝模組
- 在 C/C++ 中,
.c
/.cpp
檔案會編譯並透過建立/make
指令碼加入函式庫。- 在 Julia 中,
import [PkgName]
/using [PkgName]
陳述式會載入位於套件[PkgName]/src/
子目錄中的[PkgName].jl
。 - 反過來,
[PkgName].jl
通常會透過呼叫include "[someotherfile].jl"
來載入相關的原始檔。
- 在 Julia 中,
include "./path/to/somefile.jl"
(Julia) 與#include "./path/to/somefile.jl"
(C/C++) 非常類似。- 不過
include "..."
(Julia) 不用於包含標頭檔(不需要)。 - 請勿使用
include "..."
(Julia) 從其他「軟體模組」載入程式碼(請改用import
/using
)。 include "path/to/some/module.jl"
(Julia) 會在不同的模組中建立相同程式碼的多個版本(建立具有相同名稱的相異類型等)。include "somefile.jl"
通常用於組裝同一個 Julia 套件(「軟體模組」)中的多個檔案。因此,確保檔案僅include
一次相對容易(沒有#ifdef
混淆)。
- 不過
Julia ⇔ C/C++:模組介面
- C++ 使用「公開」
.h
/.hpp
檔案公開介面,而 Juliamodule
則將特定符號標記為public
或export
ed,供使用者使用。- Julia
module
通常只透過為現有函式產生新的「方法」來新增功能(例如:Base.push!
)。 - 因此,Julia 套件的開發人員無法依賴標頭檔進行介面文件編寫。
- Julia 套件的介面通常使用文件字串、README.md、靜態網頁等來描述。
- Julia
- 有些開發人員選擇不
export
套件/模組所需的全部符號。- 使用者可能會預期透過使用套件/模組名稱來取得這些元件的資格函數/結構/...(例如:
MyModule.run_this_task(...)
)。
- 使用者可能會預期透過使用套件/模組名稱來取得這些元件的資格函數/結構/...(例如:
Julia ⇔ C/C++:快速參考
軟體概念 | Julia | C/C++ |
---|---|---|
未命名範圍 | begin ... end | { ... } |
函數範圍 | function x() ... end | int x() { ... } |
全域範圍 | module MyMod ... end | namespace MyNS { ... } |
軟體模組 | Julia「套件」 | .h /.hpp 檔案<br>+編譯的 somelib.a |
組裝<br>軟體模組 | SomePkg.jl :...<br>import("subfile1.jl") <br>import("subfile2.jl") <br>... | $(AR) *.o ⇒ somelib.a |
匯入<br>軟體模組 | 匯入 SomePkg | #include <somelib> <br>+連結到 somelib.a |
模組函式庫 | LOAD_PATH[] 、*Git 儲存庫,<br>**自訂套件登錄 | 更多 .h /.hpp 檔案<br>+更大的編譯 somebiglib.a |
* Julia 套件管理員支援從單一 Git 儲存庫註冊多個套件。<br> * 這允許使用者將相關套件的函式庫置於單一儲存庫中。<br> ** Julia 登錄主要設計用於提供套件的版本控制和發行。<br> ** 自訂套件登錄可用於建立一種模組函式庫類型。
與 Common Lisp 的顯著差異
Julia 預設使用 1 為基礎的陣列索引,而且它也可以處理任意 索引偏移。
函數和變數共用同一個命名空間(「Lisp-1」)。
有一個
Pair
類型,但它並非用作COMMON-LISP:CONS
。各種可迭代集合可以在語言的大多數部分中互換使用(例如,展開、元組等)。對於異質元素的短集合,Tuple
最接近 Common Lisp 列表。使用NamedTuple
取代 alist。對於同質類型的較大集合,應使用Array
和Dict
。用於建立原型的典型 Julia 工作流程也使用影像的連續操作,這透過 Revise.jl 套件實作。
為了效能,Julia 偏好運算具有 類型穩定性。Common Lisp 從底層機器運算中抽象出來,而 Julia 則更接近它們。例如
模組(命名空間)可以是階層式的。
import
和using
具有雙重角色:它們載入程式碼並在命名空間中提供程式碼。僅對模組名稱使用import
是可行的(大致等同於ASDF:LOAD-OP
)。不需要另外匯出槽位名稱。無法從模組外部指派全域變數(除了使用eval(mod, :(var = val))
作為跳脫艙口)。巨集以
@
開頭,與 Common Lisp 相比,它們並未無縫整合到語言中;因此,巨集的使用並不像在後者中那樣廣泛。語言支援 巨集 的一種衛生形式。由於表面語法不同,沒有等效於COMMON-LISP:&BODY
的東西。所有函數都是泛函的,並使用多重分派。引數清單不必遵循相同的範本,這會導致強大的慣用語法(請參閱
do
)。選用和關鍵字引數的處理方式不同。方法歧義並不像在 Common Lisp 物件系統中那樣得到解決,這需要為交集定義更具體的方法。符號不屬於任何套件,也不包含任何值本身。
M.var
在模組M
中評估符號var
。語言完全支援函數式程式設計風格,包括閉包,但這並不總是 Julia 的慣用語法解決方案。修改捕獲變數時,可能需要一些 解決方法 來提高效能。