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には存在しません。代わりに、ユーザーはオプション引数やキーワード引数を使用して同様の機能を実現できます。ジュリアは真の一次元配列を持っています。列ベクトルはサイズ
Nであり、Nx1ではありません。例えば、rand(N)は一次元配列を作成します。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、prod、およびmaximumのような還元は、sum(A)のように単一の引数で呼び出されたときに、配列のすべての要素に対して実行されます。たとえAが複数の次元を持っていてもです。Juliaでは、引数がゼロの関数を呼び出すには、
rand()のように括弧を使用する必要があります。Juliaはステートメントの終了にセミコロンを使用することを推奨していません。ステートメントの結果は自動的に表示されず(インタラクティブプロンプトを除く)、コードの行はセミコロンで終わる必要はありません。
printlnまたは@printfを使用して特定の出力を表示できます。In Juliaでは、
AとBが配列の場合、論理比較演算子のようなA == Bはブール値の配列を返しません。代わりに、A .== Bを使用し、他のブール演算子についても同様です。例えば、<、>。Juliaでは、スカラー値関数を配列に要素ごとに適用したい場合、ブロードキャスティング構文を使用します:
f.(A)の代わりにf(A)を使用します。場合によっては、両方の操作が定義されていますが、意味が異なります:MATLABではexp(A)は要素ごとに適用され、expm(A)は matrix exponential ですが、Juliaではexp.(A)が要素ごとに適用され、exp(A)は行列指数関数です。Juliaでは、演算子
&、|、および⊻(xor)は、MATLABにおけるand、or、xorに相当するビット演算を実行します。また、これらの演算子はPythonのビット演算子と同様の優先順位を持ちます(Cとは異なります)。スカラーまたは配列全体に対して要素ごとに操作を行うことができ、論理配列を組み合わせるために使用できますが、演算の順序に違いがあることに注意してください:例えば、Aの要素が1または2に等しいものを選択するには、(A .== 1) .| (A .== 2)のように括弧が必要です。Juliaでは、コレクションの要素をスプラット演算子
...を使用して関数に引数として渡すことができます。例えば、xs=[1,2]; f(xs...)のようにします。Juliaの
svdは、特異値を密な対角行列ではなくベクトルとして返します。Juliaでは、
...はコードの行を続けるために使用されません。代わりに、不完全な式は自動的に次の行に続きます。JuliaとMATLABの両方で、変数
ansはインタラクティブセッションで発行された最後の式の値に設定されます。MATLABとは異なり、Juliaでは非インタラクティブモードでJuliaコードが実行されるとansは設定されません。Juliaの
structは、MATLABのclassとは異なり、実行時にフィールドを動的に追加することをサポートしていません。代わりに、Dictを使用してください。Juliaの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では次のように実現できます
A(A < threshold) = 0。Juliaの同等のコードはA[A .< threshold] .= 0です。セル配列のすべての要素を抽出(または「デリファレンス」)するアナロジーは、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
Juliaの目標の一つは、データ分析と統計プログラミングのための効果的な言語を提供することです。Rから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)と同等です。 - In 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の
->は無名関数を作成します。Juliaの
*演算子は、Rとは異なり行列の乗算を行うことができます。もしAとBが行列であれば、A * BはJuliaにおける行列の乗算を示し、RのA %*% Bに相当します。Rでは、この同じ表記は要素ごとの(アダマール)積を実行します。要素ごとの乗算操作を行うには、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を提供していません。代わりに、nrow(M)にはsize(M, 1)を、ncol(M)にはsize(M, 2)を使用してください。Juliaはスカラー、ベクトル、行列を区別することに注意しています。Rでは、
1とc(1)は同じです。Juliaでは、それらを互換的に使用することはできません。Juliaでは、代入操作の左辺に関数呼び出しの結果を代入することはできません:
diag(M) = fill(1, n)のように書くことはできません。Juliaは、メイン名前空間に関数を追加することを推奨していません。Juliaのほとんどの統計機能は、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を置き換えますが、重要な違いがあります。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ですが、リストのようにではなくスカラー値のように振る舞います。is.null(x)の代わりにx === nothingを使用してください。In Julia、欠損値は
missingオブジェクトによって表され、NAではありません。ベクトルに対する要素ごとの操作にはismissing(x)(またはismissing.(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}で表現できます。これには、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を必要とします。Juliaのx[2:end]はPythonのx[1:]に相当します。 - 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]]のように配列で行列をインデックス指定すると、最初と2番目の行と最初と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ではNumPyに対してループの順序を逆にする必要があります(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では、スカラー値関数を配列に要素ごとに適用したい場合、ブロードキャスティング構文を使用します:
f.(A)の代わりにf(A)を使用します。場合によっては、両方の操作が定義されていますが、意味が異なります:numpy.exp(A)は要素ごとに適用され、scipy.linalg.expm(A)は matrix exponential ですが、Juliaではexp.(A)が要素ごとに適用され、exp(A)は行列指数関数です。 - 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のループは高速であり、パフォーマンスの理由から「ベクトル化」されたコードを書く必要はありません。
- 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)は単独で呼び出すと、常にIntを返すのではなく、xと同じ型の整数値を返します。 - 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++の配列はデフォルトで行優先です。配列をループする際に最適なパフォーマンスを得るためには、ループの順序をC/C++に対してJuliaでは逆にする必要があります(relevant section of Performance Tipsを参照)。
- Juliaの値は、代入または関数に渡されたときにコピーされません。関数が配列を変更すると、その変更は呼び出し元に表示されます。
- Juliaでは、空白が重要であり、C/C++とは異なるため、Juliaプログラムから空白を追加または削除する際には注意が必要です。
- Juliaでは、小数点のないリテラル数(例えば
42)は符号付き整数を作成し、型はIntですが、マシンワードサイズに収まらないほど大きなリテラルは、自動的により大きなサイズの型に昇格されます。例えば、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桁は型UInt64、17から32桁は型UInt128、32桁を超えるものは型BigIntになります。これは、16進マスクを定義する際に考慮する必要があります。例えば、~0xf == 0xf0は~0x000f == 0xfff0とは非常に異なります。64ビットのFloat64および32ビットのFloat32ビットリテラルは、それぞれ1.0および1.0f0として表現されます。浮動小数点リテラルは、正確に表現できない場合は丸められ(BigFloat型には昇格されません)、浮動小数点リテラルの動作はC/C++に近いです。8進(0oで接頭辞)および2進(0bで接頭辞)のリテラルも符号なし(または128ビットを超える場合はBigInt)として扱われます。 - In 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)のように)。 - Juliaは、ステートメントを終了するためにセミコロンを使用する必要はありません。式の結果は自動的に表示されることはなく(インタラクティブプロンプト、つまりREPLを除く)、コードの行はセミコロンで終わる必要はありません。
printlnまたは@printfを使用して特定の出力を表示できます。REPLでは、;を使用して出力を抑制できます。;は[ ]内では異なる意味を持つため、注意が必要です。;は単一行の式を区切るために使用できますが、多くの場合、厳密には必要なく、可読性を助けるためのものです。 - Juliaでは、演算子
⊻(xor) はビット単位のXOR演算を実行します。つまり、C/C++では^です。また、ビット単位の演算子はC/C++と同じ優先順位を持たないため、括弧が必要な場合があります。 - Juliaの
^は、C/C++のビット単位のXORではなく、累乗(pow)です(Juliaでは⊻または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。マクロ構文の終わりが不明な場合は、関数のような形式を使用してください。 - Juliaには、マクロ
@enum(name, value1, value2, ...)を使用して表現される列挙型があります。例えば:@enum(Fruit, banana=1, apple, pear) - 慣例として、引数を変更する関数は名前の末尾に
!が付いています。例えばpush!です。 - C++では、デフォルトで静的ディスパッチが行われます。つまり、動的ディスパッチを行うためには、関数を仮想として注釈を付ける必要があります。一方、Juliaではすべてのメソッドが「仮想」です(ただし、これはより一般的で、メソッドは
thisだけでなく、すべての引数の型に基づいてディスパッチされ、最も特異的な宣言ルールが適用されます)。
Julia ⇔ C/C++: Namespaces
C/C++
namespaces はおおよそ Juliamodules に対応しています。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
Juliaはデフォルトで配列に1ベースのインデックスを使用し、任意の index offsets を処理することもできます。
関数と変数は同じ名前空間(「Lisp-1」)を共有します。
Pair型は存在しますが、COMMON-LISP:CONSとして使用することは意図されていません。さまざまな反復可能なコレクションは、言語のほとんどの部分で互換的に使用できます(例:スプラッティング、タプルなど)。Tupleは、短い異種要素のコレクションに対してCommon Lispリストに最も近いものです。alistの代わりに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 が必要になる場合があります。