Noteworthy Differences from other Languages
Noteworthy differences from 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
はドメインエラーを引き起こします。結果が整数ではないためです(詳細についてはthe FAQ entry on domain errorsを参照してください)。Juliaでは、複数の値がタプルとして返され、割り当てられます。例えば、
(a, b) = (1, 2)
やa, b = 1, 2
のようにです。MATLABのnargout
は、返される値の数に基づいてオプションの作業を行うためにMATLABでよく使用されますが、Juliaには存在しません。代わりに、ユーザーはオプション引数やキーワード引数を使用して同様の機能を実現できます。ジュリアは真の1次元配列を持っています。列ベクトルはサイズ
N
であり、Nx1
ではありません。例えば、rand(N)
は1次元配列を作成します。Juliaでは、
[x,y,z]
は常にx
、y
、z
を含む3要素の配列を構築します。- 最初の(「垂直」)次元で連結するには、
vcat(x,y,z)
を使用するか、セミコロンで区切ります([x; y; z]
)。 - 2次元("水平")で連結するには、
hcat(x,y,z)
を使用するか、スペースで区切ります([x y z]
)。 - ブロック行列を構築するには(最初の2つの次元で連結する)、
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(A)
のように単一の引数で呼び出すと、sum
、prod
、およびmaximum
のような還元が配列のすべての要素に対して実行されます。たとえA
が複数の次元を持っていてもです。Juliaでは、引数がゼロの関数を呼び出すには、
rand()
のように括弧を使用する必要があります。Juliaはステートメントの終了にセミコロンを使用することを推奨していません。ステートメントの結果は自動的に表示されず(インタラクティブプロンプトを除く)、コードの行はセミコロンで終わる必要はありません。
println
または@printf
を使用して特定の出力を表示できます。Juliaでは、
A
とB
が配列の場合、論理比較演算子のようなA == B
はブール値の配列を返しません。代わりに、A .== B
を使用し、他のブール演算子についても同様です。例えば、<
や>
のように。Juliaでは、演算子
&
、|
、および⊻
(xor
)は、MATLABにおけるand
、or
、xor
に相当するビット演算を行います。また、Cとは異なり、Pythonのビット演算子と同様の優先順位を持っています。これらはスカラーまたは配列に対して要素ごとに操作を行うことができ、論理配列を組み合わせるために使用できますが、演算の順序に違いがあることに注意してください:括弧が必要な場合があります(例:A
の要素が1または2に等しいものを選択するには、(A .== 1) .| (A .== 2)
を使用します)。Juliaでは、コレクションの要素をスプラット演算子
...
を使用して関数に引数として渡すことができます。例えば、xs=[1,2]; f(xs...)
のようにします。ジュリアの
svd
は、特異値を密な対角行列ではなくベクトルとして返します。Juliaでは、
...
はコードの行を続けるために使用されません。代わりに、不完全な式は自動的に次の行に続きます。JuliaとMATLABの両方では、変数
ans
はインタラクティブセッションで発行された最後の式の値に設定されます。MATLABとは異なり、Juliaでは非インタラクティブモードでJuliaコードが実行されるとans
は設定されません。ジュリアの
struct
は、MATLAB のclass
と異なり、実行時にフィールドを動的に追加することをサポートしていません。代わりに、Dict
を使用してください。ジュリアの Dict は順序付けされていません。Juliaでは、各モジュールが独自のグローバルスコープ/名前空間を持っていますが、MATLABでは1つのグローバルスコープしかありません。
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]
の代替手段となります。4d61726b646f776e2e436f64652822222c202266696c746572212229_40726566
を使用すると、一時配列の使用が減ります。セル配列のすべての要素を抽出(または「デリファレンス」)するアナロジーは、MATLABでは
vertcat(A{:})
のように書かれ、Juliaではスプラット演算子を使用してvcat(A...)
のように書かれます。Juliaでは、
adjoint
関数は共役転置を実行します。MATLABでは、adjoint
は「随伴行列」または古典的随伴行列を提供し、これは余因子の行列の転置です。Juliaでは、a^b^cはa^(b^c)として評価されますが、MATLABでは(a^b)^cとなります。
Noteworthy differences from R
ジュリアの目標の一つは、データ分析と統計プログラミングのための効果的な言語を提供することです。Rからジュリアに移行するユーザーにとって、いくつかの注目すべき違いがあります:
ジュリアのシングルクォートは文字を囲むものであり、文字列ではありません。
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,:]
のようにします。ジュリアの
map
は、関数を最初に取り、その後に引数を取ります。これは、Rのlapply(<structure>, function, ...)
とは異なります。同様に、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は、
if
のような条件ブロック、while
/for
のようなループブロック、そして関数の終わりを示すためにend
を使用します。1行のif ( cond ) statement
の代わりに、Juliaはif cond; statement; end
、cond && statement
、および!cond || statement
の形式の文を許可します。後者の2つの構文では、代入文は明示的に括弧で囲む必要があります。例えば、cond && (x = value)
のようになります。Juliaでは、
<-
、<<-
、および->
は代入演算子ではありません。Juliaの
->
は無名関数を作成します。ジュリアの
*
演算子は、Rとは異なり行列の乗算を行うことができます。もしA
とB
が行列であれば、A * B
はジュリアにおける行列の乗算を示し、RのA %*% B
に相当します。Rでは、この同じ表記は要素ごとの(アダマール)積を実行します。要素ごとの乗算操作を行うには、ジュリアでは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
を提供していません。代わりに、nrow(M)
にはsize(M, 1)
を、ncol(M)
にはsize(M, 2)
を使用してください。Juliaはスカラー、ベクトル、行列を区別することに注意しています。Rでは、
1
とc(1)
は同じですが、Juliaでは互換的に使用することはできません。Juliaでは、代入操作の左辺に関数呼び出しの結果を割り当てることはできません:
diag(M) = fill(1, n)
のように書くことはできません。ジュリアは、メイン名前空間に関数を追加することを避けるようにしています。ジュリアのほとんどの統計機能は、packages の下にある JuliaStats organization にあります。例えば:
- 確率分布に関連する関数は、Distributions packageによって提供されています。
- DataFrames package はデータフレームを提供します。
- 一般化線形モデルは、GLM packageによって提供されています。
Juliaはタプルと実際のハッシュテーブルを提供しますが、Rスタイルのリストは提供していません。複数のアイテムを返す際は、通常タプルまたは名前付きタプルを使用すべきです:
list(a = 1, b = 2)
の代わりに、(1, 2)
または(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)
に相当します。ジュリアの
max
とmin
は、Rにおけるpmax
とpmin
の同等物ですが、両方の引数は同じ次元である必要があります。一方、maximum
とminimum
は、Rにおけるmax
とmin
を置き換えますが、重要な違いがあります。ジュリアの
sum
、prod
、maximum
、およびminimum
は、Rの対応するものとは異なります。これらはすべてオプションのキーワード引数dims
を受け入れ、操作が行われる次元を示します。たとえば、ジュリアで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
ですが、リストのようにではなくスカラー値のように振る舞います。is.null(x)
の代わりにx === nothing
を使用してください。In 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]
の代わりに書くことができます。4d61726b646f776e2e436f64652822222c202266696c746572212229_40726566
を使用することで、一時配列の使用を減らすことができます。
Noteworthy differences from Python
- Juliaの
for
、if
、while
などのブロックは、end
キーワードで終了します。インデントのレベルは、Pythonのように重要ではありません。Pythonとは異なり、Juliaにはpass
キーワードはありません。 - 文字列は、Juliaでは二重引用符(
"text"
)で表され(複数行の文字列には三重の二重引用符を使用)、Pythonでは単一の引用符('text'
)または二重引用符("text"
)のいずれかで表されます。Juliaでは、単一の引用符は文字を表すために使用されます('c'
)。 - 文字列の連結は、Pythonのように
+
ではなく、Juliaでは*
で行います。同様に、文字列の繰り返しは*
ではなく^
で行います。Pythonのような文字列リテラルの暗黙の連結(例:'ab' 'cd' == 'abcd'
)は、Juliaでは行われません。 - 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では、配列や文字列などのインデックスは0ベースではなく1ベースです。
- Juliaのスライスインデックスは、Pythonとは異なり最後の要素を含みます。
a[2:3]
はJuliaではa[1:3]
に相当します。 - Pythonとは異なり、Juliaでは AbstractArrays with arbitrary indexes のように負のインデックスを特別に解釈することはありません。Pythonの負のインデックスの解釈、
a[-1]
とa[-2]
は、Juliaではa[end]
とa[end-1]
と書く必要があります。 - Juliaは最後の要素までのインデックスに
end
を必要とします。Pythonのx[1:]
はJuliaのx[2:end]
に相当します。 - In 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順)です。配列をループする際に最適なパフォーマンスを得るためには、ループの順序をNumPyに対してJuliaでは逆にする必要があります(relevant section of Performance Tipsを参照)。
- Juliaの更新演算子(例:
+=
、-=
など)はインプレースではなく、NumPyのものはインプレースです。これは、A = [1, 1]; B = A; B += [3, 3]
がA
の値を変更せず、むしろB
の名前を右辺の結果B = B + 3
に再バインドすることを意味します。これは新しい配列です。インプレース操作を行うには、B .+= 3
を使用するか(詳細はdot operatorsを参照)、明示的なループ、またはInplaceOps.jl
を使用してください。 - Juliaは、メソッドが呼び出されるたびに関数引数のデフォルト値を評価しますが、Pythonでは関数が定義されるときにデフォルト値が一度だけ評価されます。例えば、関数
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)
は、Python のj
ではなく、Julia ではim
として表されます。 - Juliaでは、累乗演算子は
^
であり、Pythonのように**
ではありません。 - Juliaは
Nothing
型のnothing
を使用してnull値を表現しますが、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のメソッド呼び出しは異なる構文を使用し、メソッドの受信者に基づいてディスパッチを許可します)。
- Juliaにはクラスはありません。その代わりに、データを含むがメソッドは持たない構造体(可変または不変)があります。
- Pythonのクラスインスタンスのメソッドを呼び出すこと(
x = MyClass(*args); x.f(y)
)は、Juliaにおける関数呼び出しに対応します。例えば、x = MyType(args...); f(x, y)
のようになります。一般的に、マルチディスパッチはPythonのクラスシステムよりも柔軟で強力です。 - Juliaの構造体は正確に1つの抽象スーパタイプを持つことができますが、Pythonのクラスは1つ以上の(抽象または具体的な)スーパークラスから継承することができます。
- 論理的なJuliaプログラムの構造(パッケージとモジュール)はファイル構造とは独立していますが、Pythonのコード構造はディレクトリ(パッケージ)とファイル(モジュール)によって定義されています。
- Juliaでは、大きなモジュールのテキストを複数のファイルに分割することが慣例であり、ファイルごとに新しいモジュールを導入することはありません。コードは、
include
を介してメインファイル内の単一のモジュールに再構成されます。Pythonの同等のもの(exec
)はこの用途には一般的ではなく(以前の定義を静かに上書きしてしまいます)、Juliaプログラムはusing
またはimport
を使用してmodule
レベルで単位として定義され、最初に必要になったときにのみ一度実行されます。これはPythonのinclude
のようなものです。これらのモジュール内では、そのモジュールを構成する個々のファイルが、意図した順序で一度リストアップしてinclude
によって読み込まれます。 - ジュリアの三項演算子
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のループは高速であり、パフォーマンスの理由から「ベクトル化」されたコードを書く必要はありません。
- ジュリアでは、特にタイトなループ内で非定数のグローバル変数に注意してください。ジュリアでは(Pythonとは異なり)ほぼメタルに近いコードを書くことができるため、グローバル変数の影響は劇的になる可能性があります(Performance Tipsを参照)。
- 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
ブロックは両方の言語で新しいローカルスコープを導入しません。
Noteworthy differences from C/C++
- Juliaの配列は角括弧でインデックス付けされ、複数の次元を持つことができます
A[i,j]
。この構文は、C/C++のポインタやアドレスへの参照のための単なる構文糖ではありません。the manual entry about array construction を参照してください。 - Juliaでは、配列や文字列などのインデックスは0ベースではなく1ベースです。
- Juliaの配列は、別の変数に代入されるとコピーされません。
A = B
の後、B
の要素を変更すると、A
も変更されます。+=
のような更新演算子はインプレースで動作せず、A = A + B
と同等であり、左辺を右辺の式の結果に再バインドします。 - Juliaの配列は列優先(Fortran順)であり、C/C++の配列はデフォルトで行優先です。配列をループする際に最適なパフォーマンスを得るためには、JuliaではC/C++に対してループの順序を逆にする必要があります(relevant section of Performance Tipsを参照)。
- Juliaの値は、割り当てられたり関数に渡されたりするときにコピーされません。関数が配列を変更すると、その変更は呼び出し元に表示されます。
- Juliaでは、空白が重要であり、C/C++とは異なるため、Juliaプログラムに空白を追加または削除する際には注意が必要です。
- Juliaでは、小数点のないリテラル数(例えば
42
)は符号付き整数を作成し、型はInt
ですが、マシンワードサイズに収まらないほど大きなリテラルは、自動的により大きなサイズの型(Int32
の場合はInt64
、Int128
、または任意の大きさのBigInt
型)に昇格されます。符号なしおよび/または符号付きを示すための数値リテラルサフィックス(L
、LL
、U
、UL
、ULL
など)はありません。10進リテラルは常に符号付きであり、16進リテラル(C/C++のように0x
で始まる)は符号なしですが、128ビットを超える場合はBigInt
型になります。16進リテラルは、C/C++/Javaとは異なり、Juliaの10進リテラルとは異なり、先頭の0を含むリテラルの長さに基づいて型が決まります。例えば、0x0
と0x00
は型UInt8
を持ち、0x000
と0x0000
は型UInt16
を持ち、5から8桁の16進数リテラルは型UInt32
、9から16桁の16進数リテラルは型UInt64
、17から32桁の16進数リテラルは型UInt128
、32桁を超える16進数リテラルは型BigInt
になります。これは、例えば~0xf == 0xf0
が~0x000f == 0xfff0
とは非常に異なるため、16進マスクを定義する際に考慮する必要があります。64ビットのFloat64
および32ビットのFloat32
ビットリテラルは、それぞれ1.0
および1.0f0
として表現されます。浮動小数点リテラルは、正確に表現できない場合は丸められ(BigFloat
型には昇格されません)、浮動小数点リテラルはC/C++に近い動作をします。8進数(0o
で接頭辞)および2進数(0b
で接頭辞)のリテラルも符号なし(または128ビットを超える場合はBigInt
)として扱われます。 - Juliaでは、除算演算子
/
は、両方のオペランドが整数型の場合に浮動小数点数を返します。整数除算を行うには、div
または÷
を使用してください。 - 浮動小数点型での
Array
のインデックス付けは、一般的にJuliaではエラーです。Cの式a[i / 2]
に相当するJuliaの表現はa[i ÷ 2 + 1]
であり、ここでi
は整数型です。 - 文字列リテラルは、
"
または"""
で区切ることができます。"""
で区切られたリテラルは、"
文字を引用符なしで含むことができます(例:"\""
)。文字列リテラルには、他の変数や式の値を埋め込むことができ、$variablename
または$(expression)
で示されます。これは、関数の文脈で変数名や式を評価します。 //
はRational
数値を示し、単一行コメント(Juliaでは#
)ではありません。#=
はマルチラインコメントの開始を示し、=#
はそれを終了します。- Juliaの関数は、最後の式または
return
キーワードから値を返します。複数の値を関数から返し、タプルとして割り当てることができます。例えば、(a, b) = myfunction()
やa, b = myfunction()
のように、C/C++のように値へのポインタを渡す必要はありません(つまり、a = myfunction(&b)
のように)。 - ジュリアは、ステートメントを終了するためにセミコロンを使用する必要はありません。式の結果は自動的に表示されることはなく(インタラクティブプロンプト、つまりREPLを除く)、コードの行はセミコロンで終わる必要はありません。
println
または@printf
を使用して特定の出力を表示できます。REPLでは、;
を使用して出力を抑制できます。;
は[ ]
内では異なる意味を持つため、注意が必要です。;
は単一の行で式を区切るために使用できますが、多くの場合、厳密には必要ではなく、可読性を助けるためのものです。 - ジュリアでは、演算子
⊻
(xor
) はビット単位のXOR演算を実行します。つまり、C/C++では^
です。また、ビット演算子の優先順位はC/C++とは異なるため、括弧が必要な場合があります。 - ジュリアの
^
は、C/C++ のビット単位の XOR ではなく、累乗(pow)です(ジュリアでは⊻
またはxor
を使用してください)。 - Juliaには2つの右シフト演算子、
>>
と>>>
があります。>>
は算術シフトを行い、>>>
は常に論理シフトを行います。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は、
if
のような条件ブロック、while
/for
のようなループブロック、そして関数の終わりを示すためにend
を使用します。1行のif ( cond ) statement
の代わりに、Juliaはif cond; statement; end
、cond && statement
、および!cond || statement
の形式の文を許可します。後者の2つの構文では、代入文は明示的に括弧で囲む必要があります。例えば、cond && (x = value)
のように、演算子の優先順位のためです。 - Juliaには行継続構文がありません。行の終わりで、これまでの入力が完全な式であれば、それは完了と見なされます。そうでない場合、入力は続きます。式を強制的に続けさせる方法の一つは、括弧で囲むことです。
- Juliaのマクロは、プログラムのテキストではなく、解析された式に対して操作を行うため、Juliaコードの高度な変換を実行することができます。マクロ名は
@
文字で始まり、関数のような構文@mymacro(arg1, arg2, arg3)
と、文のような構文@mymacro arg1 arg2 arg3
の両方を持っています。これらの形式は相互に置き換え可能であり、特にマクロが別の式内に現れる場合、関数のような形式が便利で、しばしば最も明確です。文のような形式は、分散for
構文のようにブロックに注釈を付けるためによく使用されます:@distributed for i in 1:n; #= body =#; end
。マクロ構文の終わりが不明確な場合は、関数のような形式を使用してください。 - ジュリアには、マクロ
@enum(name, value1, value2, ...)
を使用して表現される列挙型があります。例えば:@enum(Fruit, banana=1, apple, pear)
- 慣例として、引数を変更する関数は名前の末尾に
!
を付けます。例えばpush!
です。 - C++では、デフォルトで静的ディスパッチが行われます。つまり、動的ディスパッチを行うためには、関数を仮想として注釈を付ける必要があります。一方、Juliaではすべてのメソッドが「仮想」です(ただし、これはより一般的なものであり、メソッドは
this
だけでなく、すべての引数の型に基づいてディスパッチされ、最も特異的な宣言ルールが適用されます)。
Julia ⇔ C/C++: Namespaces
C/C++
namespace
s はおおよそ Juliamodule
s に対応しています。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++: Module loading
C/C++の「ライブラリ」を考えると、あなたはおそらくJuliaの「パッケージ」を探しているでしょう。
- 注意: C/C++ライブラリはしばしば複数の「ソフトウェアモジュール」を含むのに対し、Juliaの「パッケージ」は通常1つを含みます。
- リマインダー: 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++: Assembling modules
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++: Module interface
C++は「public」
.h
/.hpp
ファイルを使用してインターフェースを公開しますが、Juliaのmodule
はユーザー向けに意図された特定のシンボルをpublic
またはexport
としてマークします。- しばしば、Juliaの
module
は既存の関数に新しい「メソッド」を生成することで機能を追加します(例:Base.push!
)。 - Juliaパッケージの開発者は、インターフェースのドキュメントとしてヘッダーファイルに依存することはできません。
- Juliaパッケージのインターフェースは、通常、ドキュメント文字列、README.md、静的ウェブページなどを使用して説明されます。
- しばしば、Juliaの
一部の開発者は、自分のパッケージ/モジュールを使用するために必要なすべてのシンボルを
export
しないことを選択しますが、エクスポートされていないユーザー向けのシンボルはpublic
としてマークするべきです。- ユーザーは、これらのコンポーネントにパッケージ/モジュール名で関数/構造体/...を修飾してアクセスすることが期待されるかもしれません(例:
MyModule.run_this_task(...)
)。
- ユーザーは、これらのコンポーネントにパッケージ/モジュール名で関数/構造体/...を修飾してアクセスすることが期待されるかもしれません(例:
Julia ⇔ C/C++: Quick reference
Software Concept | Julia | C/C++ |
---|---|---|
unnamed scope | begin ... end | { ... } |
function scope | function x() ... end | int x() { ... } |
global scope | module MyMod ... end | namespace MyNS { ... } |
software module | A Julia "package" | .h /.hpp files<br>+compiled somelib.a |
assembling<br>software modules | SomePkg.jl : ...<br>import("subfile1.jl") <br>import("subfile2.jl") <br>... | $(AR) *.o ⇒ somelib.a |
import<br>software module | import SomePkg | #include <somelib> <br>+link in somelib.a |
module library | LOAD_PATH[] , *Git repository,<br>**custom package registry | more .h /.hpp files<br>+bigger compiled somebiglib.a |
- The Julia package manager supports registering multiple packages from a single Git repository.<br> * This allows users to house a library of related packages in a single repository.<br> ** Julia registries are primarily designed to provide versioning \& distribution of packages.<br> ** Custom package registries can be used to create a type of module library.
Noteworthy differences from Common Lisp
ジュリアはデフォルトで配列に1ベースのインデックスを使用し、任意の index offsets も扱うことができます。
関数と変数は同じ名前空間(「Lisp-1」)を共有します。
Pair
型は存在しますが、COMMON-LISP:CONS
として使用することは意図されていません。さまざまな反復可能なコレクションは、言語のほとんどの部分で互換的に使用できます(例:スプラッティング、タプルなど)。Tuple
は、異種要素の短いコレクションに対して Common Lisp リストに最も近いものです。alists
の代わりにNamedTuple
を使用してください。均質な型の大きなコレクションには、Array
とDict
を使用するべきです。典型的なJuliaのプロトタイピングワークフローでは、Revise.jlパッケージを使用して、画像の継続的な操作も行います。
パフォーマンスのために、Juliaは操作が type stability であることを好みます。Common Lispは基盤となる機械操作から抽象化しますが、Juliaはそれにより近いです。例えば:
整数の除算は
/
を使用すると、計算が正確であっても常に浮動小数点の結果を返します。//
は常に有理数の結果を返します。÷
は常に(切り捨てられた)整数結果を返します。
Bignumsはサポートされていますが、変換は自動ではありません; 通常の整数 overflow。
複素数はサポートされていますが、複素的な結果を得るには、you need complex inputs。
複素数型と有理数型が複数あり、異なるコンポーネント型があります。
モジュール(名前空間)は階層的にすることができます。
import
とusing
は二重の役割を持っています:それらはコードをロードし、名前空間で利用可能にします。モジュール名のみのimport
は可能です(おおよそASDF:LOAD-OP
に相当します)。スロット名は別々にエクスポートする必要はありません。グローバル変数はモジュールの外部から割り当てることはできません(eval(mod, :(var = val))
を使用することで逃げ道として可能です)。マクロは
@
で始まり、Common Lisp のように言語にシームレスに統合されていないため、マクロの使用は後者ほど広範ではありません。macros の一種の衛生が言語によってサポートされています。異なる表面構文のため、COMMON-LISP:&BODY
に相当するものはありません。すべての関数は汎用的であり、複数のディスパッチを使用します。引数リストは同じテンプレートに従う必要がなく、これにより強力なイディオムが生まれます(
do
を参照)。オプション引数とキーワード引数は異なる方法で処理されます。メソッドの曖昧さは、Common Lisp Object Systemのようには解決されず、交差点のためにより具体的なメソッドの定義が必要です。シンボルはどのパッケージにも属さず、本質的に 値を含んでいません。
M.var
はモジュールM
内のシンボルvar
を評価します。言語は関数型プログラミングスタイルを完全にサポートしており、クロージャも含まれていますが、必ずしもJuliaにとって慣用的な解決策ではありません。キャプチャされた変数を変更する際には、パフォーマンスのためにいくつかの workarounds が必要になる場合があります。