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のように ij ではありません。

  • 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]は常にxyzを含む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:cAbstractRangeオブジェクトを構築します。MATLABのように完全なベクターを構築するには、collect(a:b)を使用します。一般的に、collectを呼び出す必要はありません。AbstractRangeオブジェクトはほとんどの場合、通常の配列のように動作しますが、値を遅延的に計算するため、より効率的です。このように完全な配列の代わりに特化したオブジェクトを作成するパターンは頻繁に使用され、rangeのような関数や、enumeratezipのようなイテレータでも見られます。特別なオブジェクトは、ほとんどの場合、通常の配列のように使用できます。

  • Juliaの関数は、関数定義で返す変数の名前を列挙するのではなく、最後の式またはreturnキーワードから値を返します(詳細はThe return Keywordを参照してください)。

  • Juliaスクリプトには任意の数の関数を含めることができ、ファイルが読み込まれるとすべての定義が外部から見えるようになります。関数の定義は、現在の作業ディレクトリの外にあるファイルから読み込むことができます。

  • Juliaでは、sum(A)のように単一の引数で呼び出すと、sumprod、およびmaximumのような還元が配列のすべての要素に対して実行されます。たとえAが複数の次元を持っていてもです。

  • Juliaでは、引数がゼロの関数を呼び出すには、rand()のように括弧を使用する必要があります。

  • Juliaはステートメントの終了にセミコロンを使用することを推奨していません。ステートメントの結果は自動的に表示されず(インタラクティブプロンプトを除く)、コードの行はセミコロンで終わる必要はありません。 println または @printf を使用して特定の出力を表示できます。

  • Juliaでは、ABが配列の場合、論理比較演算子のようなA == Bはブール値の配列を返しません。代わりに、A .== Bを使用し、他のブール演算子についても同様です。例えば、<>のように。

  • Juliaでは、演算子 &|、および xor)は、MATLABにおける andorxor に相当するビット演算を行います。また、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はfilterfilter!という高階関数を提供しており、ユーザーは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]を生成します。
  • 多くの言語と同様に、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; endcond && statement、および!cond || statementの形式の文を許可します。後者の2つの構文では、代入文は明示的に括弧で囲む必要があります。例えば、cond && (x = value)のようになります。

  • Juliaでは、<-<<-、および -> は代入演算子ではありません。

  • Juliaの->は無名関数を作成します。

  • ジュリアの * 演算子は、Rとは異なり行列の乗算を行うことができます。もし AB が行列であれば、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は数字01をブール値として扱いません。Juliaではif (1)を書くことはできません。なぜなら、if文はブール値のみを受け入れるからです。代わりに、if trueif Bool(1)、またはif 1==1と書くことができます。

  • Juliaはnrowncolを提供していません。代わりに、nrow(M)にはsize(M, 1)を、ncol(M)にはsize(M, 2)を使用してください。

  • Juliaはスカラー、ベクトル、行列を区別することに注意しています。Rでは、1c(1)は同じですが、Juliaでは互換的に使用することはできません。

  • ジュリアの diagdiagm はRのものとは異なります。

  • Juliaでは、代入操作の左辺に関数呼び出しの結果を割り当てることはできません:diag(M) = fill(1, n)のように書くことはできません。

  • ジュリアは、メイン名前空間に関数を追加することを避けるようにしています。ジュリアのほとんどの統計機能は、packages の下にある JuliaStats organization にあります。例えば:

  • 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では、ベクトルと行列はhcatvcat、およびhvcatを使用して連結されます。Rのようにcrbind、およびcbindは使用しません。

  • Juliaでは、a:bのような範囲はRのようなベクトルの省略形ではなく、反復処理に使用される特化したAbstractRangeオブジェクトです。範囲をベクトルに変換するには、collect(a:b)を使用します。

  • : 演算子は R と Julia で異なる優先順位を持っています。特に、Julia では算術演算子が : 演算子よりも高い優先順位を持ちますが、R ではその逆です。例えば、Julia の 1:n-1 は R の 1:(n-1) に相当します。

  • ジュリアの maxmin は、Rにおける pmaxpmin の同等物ですが、両方の引数は同じ次元である必要があります。一方、maximumminimum は、Rにおける maxmin を置き換えますが、重要な違いがあります。

  • ジュリアの sumprodmaximum、および 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) となります。第二引数に関してはエラーチェックが行われないことに注意が必要です。

  • ジュリアには、引数を変更できるいくつかの関数があります。たとえば、sortsort! の両方があります。

  • 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のassigngetに相当するものがありません。

  • Juliaでは、returnは括弧を必要としません。

  • Rでは、不要な値を削除するための慣用的な方法は、論理インデックスを使用することです。例えば、x[x>3]のような式や、x = x[x>3]のような文を使ってxをその場で修正します。対照的に、Juliaは高階関数filterfilter!を提供しており、ユーザーはfilter(z->z>3, x)filter!(z->z>3, x)を使って、対応する翻訳x[x.>3]x = x[x.>3]の代わりに書くことができます。4d61726b646f776e2e436f64652822222c202266696c746572212229_40726566を使用することで、一時配列の使用を減らすことができます。

Noteworthy differences from Python

  • Juliaのforifwhileなどのブロックは、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配列のような「高速」配列は、要素をインプレースで格納します(つまり、dtypenp.float64[('f1', np.uint64), ('f2', np.int32)]など)で、Array{T}で表現できます。ここでTは具体的で不変の要素型です。これにはFloat64Int32Int64などの組み込み型だけでなく、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となります。より大きな値が必要な場合は、Int128BigInt、またはFloat64のような浮動小数点型を使用してください。
  • 虚数単位 sqrt(-1) は、Python の j ではなく、Julia では im として表されます。
  • Juliaでは、累乗演算子は^であり、Pythonのように**ではありません。
  • JuliaはNothing型のnothingを使用してnull値を表現しますが、PythonはNoneType型のNoneを使用します。
  • Juliaでは、行列型に対する標準演算子は行列演算ですが、Pythonでは標準演算子は要素ごとの演算です。ABの両方が行列である場合、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における例外処理は、trycatchfinallyを使用して行われ、tryexceptfinallyは使用されません。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では、ほとんどのコードブロック(ループやtrycatchfinallyを含む)によって新しいローカルスコープが導入されます。リストやジェネレーターなどの内包表記は、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の場合はInt64Int128、または任意の大きさのBigInt型)に昇格されます。符号なしおよび/または符号付きを示すための数値リテラルサフィックス(LLLUULULLなど)はありません。10進リテラルは常に符号付きであり、16進リテラル(C/C++のように0xで始まる)は符号なしですが、128ビットを超える場合はBigInt型になります。16進リテラルは、C/C++/Javaとは異なり、Juliaの10進リテラルとは異なり、先頭の0を含むリテラルの長さに基づいて型が決まります。例えば、0x00x00は型UInt8を持ち、0x0000x0000は型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は数字01をブール値として扱いません。Juliaではif (1)を書くことはできません。なぜなら、if文はブール値のみを受け入れるからです。代わりに、if trueif Bool(1)、またはif 1==1と書くことができます。
  • Juliaは、ifのような条件ブロック、while/forのようなループブロック、そして関数の終わりを示すためにendを使用します。1行のif ( cond ) statementの代わりに、Juliaはif cond; statement; endcond && 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++ namespaces はおおよそ Julia modules に対応しています。

  • Juliaにはプライベートなグローバル変数やフィールドはありません。すべては完全修飾パス(または必要に応じて相対パス)を通じて公開されてアクセス可能です。

  • using MyNamespace::myfun (C++) はおおよそ import MyModule: myfun (Julia) に相当します。

  • using namespace MyNamespace (C++) はおおよそ using MyModule (Julia) に相当します。

    • Juliaでは、exportされたシンボルのみが呼び出し元のモジュールで利用可能になります。
    • C++では、含まれている(公開)ヘッダーファイルに見つかった要素のみが利用可能になります。
  • 注意: import/using キーワード (Julia) はモジュールも ロード します (下記参照)。

  • 注意: import/using (Julia) はグローバルスコープレベル(module)でのみ機能します。

    • C++では、using namespace Xは任意のスコープ内(例:関数スコープ)で機能します。

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を追加するのに似ています)。
  • ディレクトリベースのパッケージリポジトリ(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" という呼び出しを使って関連するソースファイルを読み込みます。
  • 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、静的ウェブページなどを使用して説明されます。
  • 一部の開発者は、自分のパッケージ/モジュールを使用するために必要なすべてのシンボルを export しないことを選択しますが、エクスポートされていないユーザー向けのシンボルは public としてマークするべきです。

    • ユーザーは、これらのコンポーネントにパッケージ/モジュール名で関数/構造体/...を修飾してアクセスすることが期待されるかもしれません(例: MyModule.run_this_task(...))。

Julia ⇔ C/C++: Quick reference

Software ConceptJuliaC/C++
unnamed scopebegin ... end{ ... }
function scopefunction x() ... endint x() { ... }
global scopemodule MyMod ... endnamespace MyNS { ... }
software moduleA Julia "package".h/.hpp files<br>+compiled somelib.a
assembling<br>software modulesSomePkg.jl: ...<br>import("subfile1.jl")<br>import("subfile2.jl")<br>...$(AR) *.o &rArr; somelib.a
import<br>software moduleimport SomePkg#include <somelib><br>+link in somelib.a
module libraryLOAD_PATH[], *Git repository,<br>**custom package registrymore .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 を使用してください。均質な型の大きなコレクションには、ArrayDict を使用するべきです。

  • 典型的なJuliaのプロトタイピングワークフローでは、Revise.jlパッケージを使用して、画像の継続的な操作も行います。

  • パフォーマンスのために、Juliaは操作が type stability であることを好みます。Common Lispは基盤となる機械操作から抽象化しますが、Juliaはそれにより近いです。例えば:

    • 整数の除算は / を使用すると、計算が正確であっても常に浮動小数点の結果を返します。

      • // は常に有理数の結果を返します。
      • ÷ は常に(切り捨てられた)整数結果を返します。
    • Bignumsはサポートされていますが、変換は自動ではありません; 通常の整数 overflow

    • 複素数はサポートされていますが、複素的な結果を得るには、you need complex inputs

    • 複素数型と有理数型が複数あり、異なるコンポーネント型があります。

  • モジュール(名前空間)は階層的にすることができます。 importusing は二重の役割を持っています:それらはコードをロードし、名前空間で利用可能にします。モジュール名のみの 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 が必要になる場合があります。