他の言語との主な違い
MATLAB との主な違い
MATLAB ユーザーは Julia の構文に親近感を覚えるかもしれませんが、Julia は MATLAB クローンではありません。両者の構文と機能は大きく異なります。MATLAB に慣れている Julia ユーザーがつまずく可能性のある主な違いをここに示します:
- Julia の配列は角括弧を使って
A[i,j]
のようにアクセスします。 - Julia の配列は他の変数に代入されてもコピーされません。
A = B
とした後にB
の要素を変更すると、A
の要素も変更されます。 - 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 はVector
を大きくするための関数push!
とappend!
を持ち、これらは MATLAB のa(end + 1) = val
よりずっと高速に動作します。 - Julia では、虚数単位
sqrt(-1)
をim
で表します。MATLAB のi
およびj
は使えません。 - Julia では、
42
のように小数点を持たない数値リテラルは浮動小数点数ではなく整数を作成します。そのため浮動小数点数を期待する関数にこのリテラルを渡すとDomainError
が発生します。例えばjulia> a = -1; 2^a
は結果が整数でないのでDomainError
となります (詳しくは FAQ のDomainError
が不必要に思える箇所で発生するのはなぜ? を参照してください)。 - Julia では、
(a, b) = (a, 2)
やa, b = 1, 2
のようにタプルを使って複数の値を返したり代入したりできます。 - MATLAB で関数の返り値の数を問い合わせるときに使う
nargout
は Julia に存在しません。同様の機能は省略可能引数やキーワード引数を使えば実現できます。 - Julia は真の一次元配列を持ちます。列ベクトルのサイズは
N
であり、Nx1
ではありません。例えばrand(N)
は一次元配列を作成します。 - Julia では、
[x,y,z]
は必ずx
,y
,z
を含んだ三要素の配列を作成します。- 一つ目の次元に関して ("垂直に") 要素を連結するには、
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
オブジェクトは値の計算を遅延させるので、通常の配列と同じように振る舞いながらも効率は通常の配列を上回ります。完全な配列ではなく特殊化されたオブジェクトを作るこのパターンは Julia で頻繁に使われており、range
などの関数やenumerate
,range
などの反復子で見られます。こういった特殊なオブジェクトは基本的に通常の配列かのように振る舞います。 - Julia の関数は最後の式または
return
キーワードに渡された式の評価結果を返します。関数の定義で返り値の変数名を宣言することはありません (詳細はマニュアルの関数の章を見てください)。 - Julia スクリプトには関数をいくつでも入れることができ、それを別のファイルから読み込むと全ての定義が外から見えるようになります。関数の定義は現在の作業ディレクトリの外側から読み込むこともできます。
- Julia では、
sum
,prod
,max
といった縮約演算に一つの配列を渡す (例えばsum(A)
と呼び出す) と、A
の次元数に関わらずA
の全要素に対する演算結果が返ります。 - Julia では、ゼロ引数で関数を呼ぶ時にも括弧が必要です。例えば
rand()
とします。 - Julia では、文の終わりのセミコロンは推奨されません。REPL でなければ文の評価結果は自動的に出力されず、コードの行がセミコロンで終わる必要もありません。値の出力には
println
や@printf
が利用できます。 - Julia では、二つの配列
A
,B
に対する論理比較演算 (A == B
など) は真偽値の配列ではなく単一の真偽値を返します。真偽値の配列を得るにはA .== B
としてください。<
,>
など他の論理演算子でも同様です。 - Julia では、
&
,|
,⊻
(xor
) といった演算子はそれぞれ MATLAB におけるand
,or
,xor
と等価なビット単位の演算を行います。論理演算子の結合の優先度は Python と同様です (C とは異なります)。Julia の論理演算子はそのままならスカラー同士に適用でき、ドットを付ければ配列の要素同士に適用できます。論理値の配列を組み合わせることも可能です。ただし結合の強さの違いに注意が必要で、括弧が必要になる場合もあります。例えば1
または2
に等しいA
の要素を選択するには(A .== 1) .| (A .== 2)
を使います。 - Julia では、splat 演算子
...
を使うとコレクションの要素は関数の引数として渡すことができます。例えばxs=[1,2]; f(xs...)
とします (参照: 可変長引数)。 - Julia の
svd
は対角行列ではなくベクトルとして特異値を返します。 - Julia では、REPL に入力するコードの行を続けるのに
...
は使いません。代わりに不完全な行を入力すると自動的に次の行の入力が受け付けられます。 - Julia と MATLAB の両方で、変数
ans
は対話セッションで最後に入力した式の値に設定されます。しかし Julia では MATLAB と異なり、コードが非対話モードで実行されるときはans
が設定されません。 - MATLAB の
class
と異なり、Julia のstruct
には実行時にフィールドを動的に追加できません。代わりにDict
を使ってください。 - Julia では各モジュールが独自のグローバルスコープ/名前空間を持ちますが、MATLAB ではグローバルスコープが一つあるだけです。
- 配列
x
から不必要な要素を取り除く MATLAB らしい書き方は、論理値の配列を添え字に使う方法です。例えばx(x>3)
とすれば要素を抽出でき、x(x>3) = []
とすればx
をインプレースに置き換えられます。これに対して Julia はfilter
やfilter!
という高階関数を提供するので、x[x.>3]
やx = x[x.>3]
ではなくfilter(z->z>3, x)
およびfilter!(z->z>3, x)
と書けます。filter!
を使うと一時的な配列を使わずに済みます。 - 高次元配列に含まれる要素を全て取り出す 「参照解除 (dereference)」 演算は MATLAB では
vertcat(A{:})
と書きますが、Julia では splat 演算子を使ってvcat(A...)
と書きます。 - Julia の
adjoint
関数は与えられた行列の共役転置を計算します。これに対して MATLAB のadjoint
関数が計算するのは与えられた行列の余因子行列 (adjugate matrix)、またの名を古典随伴行列 (classical adjoint matrix) です。これは余因子行列の転置であり、共役転置とは異なります。
R との主な違い
Julia の目標の一つはデータ解析と統計プログラミングを行うための効率的な言語を提供することです。R を使ったことがある Julia ユーザーのために、主な違いをここに示します:
-
Julia の一重引用符は文字列ではなく文字を囲みます。
-
Julia では、文字列に対する添え字アクセスで部分文字列を作成できます。R では、部分文字列を作る前に文字のベクトルへの変換が必要です。
-
Julia では、R とは異なり (Python と同様に) 文字列を三つの二重引用符
""" ... """
で構築できます。この構文は改行を含む文字列の構築に便利です。 -
Julia では可変長引数を splat 演算子
...
で指定し、その前に変数の名前を置きます。これに対して R では...
が独立して使われます。 -
Julia ではモジュロ演算が
mod(a, b)
であり、a %% b
ではありません。Julia の%
は剰余演算子です。 -
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 コードです。しかし同様の Julia コード[1, 2, 3, 4] + [1, 2]
はエラーとなります。 -
Julia では、コードの意味が変わらないなら式の最後にコンマを付けることができます。配列への添え字アクセスでこの仕様が R ユーザーを混乱させる可能性があります。例えば R で
x[1,]
は行列の最初の行を返しますが、Julia ではコンマが無視されるのでx[1,] == x[1]
であり、返り値はx
の第一要素です。行を取り出すときは:
を使ってx[1,:]
としてください。 -
Julia の
map
は関数を第一引数に受け取るので、R のlapply(<structure>, function, ...)
とは順番が異なります。同様に R のapply(X, MARGIN, FUN, ...)
に対応する Julia 関数mapslices
も関数が第一引数です。 -
R では
mapply(choose, 11:13, 1:3)
で multivariate apply (配列要素ごとの多変数関数の適用) を行いますが、Julia ではbroadcast(binomial, 11:13, 1:3)
で行います。また Julia には関数をベクトル化するためのドット構文も用意されており、同じ処理を簡潔にbinomial.(11:13, 1:3)
と書けます。 -
Julia は条件分岐ブロック (
if
など) ・ループブロック (while
とfor
)・関数 (function
) の終端をend
で表します。R のif ( cond ) statement
というワンライナーに対応する Julia の文はif cond; statement; end
,cond && statement
,!cond || statement
です。最後の二つの書き方で代入文を使うときはcond && (x = value)
のように括弧で囲む必要があります。 -
Julia では
<-
,<<-
,->
は代入演算子ではありません。 -
Julia の
->
は無名関数を作成します。 -
Julia では角括弧でベクトルを構築します。Julia の
[1, 2, 3]
は R のc(1, 2, 3)
と同じです。 -
R と異なり、Julia の
*
演算子は行列乗算を行います。A
とB
が行列のときA * B
は行列乗算となり、R のA %*% B
と同じ意味になります。R のA * B
は要素ごとの乗算 (アダマール積) を意味します。Julia で要素ごとの乗算演算を行うにはA .* B
と書いてください。 -
Julia の
transpose
関数は行列の転置を計算し、'
演算子とadjoint
関数は行列の共役転置を計算します。Julia のtranspose(A)
は R のt(A)
と等価です。また Julia には再帰的でない転置を行うpermutedims
関数があります。 -
Julia では
if
文やfor
/while
ループで括弧が必要ありません。for (i in c(1, 2, 3))
あるいはif (i == 1)
ではなくfor i in [1, 2, 3]
あるいはif i == 1
と書いてください。 -
Julia は
0
と1
を真偽値として扱いません。Julia のif
は真偽値だけを受け取るのでif (1)
と書くことはできず、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 では1
と[1]
を入れ替えて使うことはできません。 -
Julia では関数呼び出しの結果を代入文の左辺にできません。例えば
diag(M) = fill(1, n)
は Julia でエラーとなります。 -
Julia はメインの名前空間に関数を追加することを推奨しません。Julia の統計機能の多くは JuliaStats organization のパッケージとして提供されます。統計パッケージの例を示します:
- 確率分布に関連する関数は Distributions.jl パッケージによって提供されます。
- DataFrames.jl パッケージはデータフレームを提供します。
- GLM.jl パッケージは一般化された線形モデルを提供します。
-
Julia はタプルと真のハッシュテーブルを提供しますが、R 風のリストは提供しません。複数の要素を返すときはタプルあるいは名前付きタプルを使ってください: そのとき Julia で使うべきは
list(a = 1, b = 2)
ではなく(1, 2)
あるいは(a=1, b=2)
です。 -
Julia はユーザーに独自の型を書くことを推奨し、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
ではありません。 -
R の範囲 (
a:b
など) はベクトルを簡単に作るための式ですが、Julia の範囲はメモリを占有することなく反復を行うための特殊なAbstractRange
オブジェクトを作成します。AbstractRange
オブジェクトをベクトルに変換するにはcollect(a:b)
とします。 -
Julia の
max
とmin
はそれぞれ R のpmax
とpmin
に対応します。ただし Julia では二つの引数が同じ次元を持つ必要があります。R のmax
とmin
には Julia のmaximum
とminimum
が対応しますが、大きな違いがあります。 -
Julia の
sum
,prod
,maximum
,minimum
は R が持つ同名の関数と同じではありません。Julia ではこの四つの関数が演算を行う次元を指定する省略可能なキーワード引数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 では、個別の関数colSums(B)
とrowSums(B)
がこの機能を提供します。キーワード引数
dims
がベクトルならdims
に含まれる全ての次元に関する和が計算され、返り値は入力と同じ次元数を持つ配列となります。例えばsum(A, dims=(1,2)) == hcat(10)
です。dims
に関してはエラーチェックが行われないことに注意してください。 -
R では、性能の向上にベクトル化が必要です。Julia では、そのほとんど逆が成り立ちます: 最も性能の高いコードがベクトル化されていないループを使って達成されることがよくあります。
-
Julia は常に先行評価であり、R が持つ遅延評価の機能はありません。多くのユーザーにとって、これはクオート解除された式やカラム名がほとんど存在しないことを意味します。
-
Julia は
NULL
をサポートしません。R のNULL
に最も近い Julia オブジェクトはnothing
ですが、nothing
はリストではなくスカラー値のように振る舞います。値との比較ではis.null(x)
ではなくx === nothing
を使ってください。 -
Julia は欠損値を
NA
ではなくmissing
オブジェクトで表します。is.ca(x)
に対応する関数はismissing(x)
(ベクトルに対する要素ごとの操作ならismissing.(x)
) です。na.rm=TRUE
にはskipmissing
が対応します (ただしfilter
など関数がskipmissing
の返り値を直接引数に取る場合もあります)。 -
R の
assign
とget
に対応する関数を Julia は持ちません。 -
Julia の
return
は括弧を必要としません。 -
配列
x
から不必要な要素を取り除く R らしい書き方は、論理値の配列を添え字を使う方法です。例えばx[x>3]
とすれば要素を抽出でき、x = x[x>3]
とすればx
をインプレースに書き換えられます。これに対して Julia はfilter
やfilter!
という高階関数を提供するので、x[x.>3]
やx = x[x.>3]
ではなくfilter(z->z>3, x)
およびfilter!(z->z>3, x)
と書けます。特にfilter!
を使うと一時的な配列を使わずに済みます。
Python との主な違い
- Julia では
for
,if
,while
といったブロックをend
キーワードで区切ります。Python のようにインデントのレベルが意味を持つことはありません。Julia にpass
キーワードは存在しません。 - 文字列を表すのに Python では一重引用符 (
'text'
) と二重引用符 ("text"
) の両方が使えますが、Julia で使えるのは二重引用符 ("text"
) だけです。複数行からなる文字列を表す三つの二重引用符 ("""text"""
) は Julia でも利用できます。 - Python では文字列の連結に
+
を使いますが、Julia では*
を使います。同様に Julia の文字列反復は*
ではなく^
です。Python にある文字列リテラルを暗黙に連結する機能 ('ab' 'cd' == 'abcd'
など) は Julia にありません。 - Python のリスト ──柔軟性がある代わりに低速なデータ構造── に対応するのは Julia の
Vector{Any}
型、より一般的に言えば、抽象型T
を要素型とするVector{T}
型です。Python には要素をインプレースに保存する "高速な" 配列もあり、例えば Numpy の配列ではdtype
がnp.float64
や[('f1', np.uint64), ('f2', np.int32)]
などとなります。これに対応する Julia の型は、不変な具象型T
を要素型とするArray{T}
型です。高速な配列の要素型として使えるのは組み込みのFloat64
,Int32
,Int64
といった型だけではなく、もっと複雑なTuple{UInt64,Float64}
やユーザー定義型も使うことができます。 - Julia では、配列や文字列などの添え字が 0 ではなく 1 から始まります。
- スライスによる添え字アクセスは Python では最後の要素を含みませんが、Julia では最後の要素を含みます。Julia の
a[2:3]
は Python のa[1:3]
と等価です。 - Julia は負の添え字をサポートしません。例えば Julia で配列が持つ最後の要素にアクセスするときに使う添え字は
end
であり、Python のように-1
は使えません。 - Julia では、最後の要素までの添え字を選択するときに
end
が必要です。例えば Python のx[1:]
は Julia のx[2:end]
と等価です。 - Python の範囲添え字アクセスは
x[start:(stop+1):step]
という形をしていますが、Julia の範囲添え字アクセスはx[start:step:stop]
という形をしています。例えば Python のx[0:10:2]
は Julia のx[1:2:10]
と等価です。また Python ではx[::-1]
が逆順になった配列を表しますが、Julia で同じことをするにはx[end:-1:1]
とします。 - Julia では、
X[[1,2], [1,3]]
のような配列を添え字とした行列へのアクセスは一番目と二番目の行および一番目と三番目の列の交差からなる部分行列を返します。これに対して Python では、X[[1,2], [1,3]]
は行列X
の要素[1,1]
と[2,3]
からなるベクトルを返します。Julia のX[[1,2], [1,3]]
は Python のX[np.ix_([0,1],[0,2])]
と同じ意味であり、Python のX[[1,2], [1,3]]
は Julia のX[[CartesianIndex(1,1), CartesianIndex(2,3)]]
と同じ意味です。 - Julia には行を継続する構文がありません。もし行末までの入力が完全な式となるなら、その式はそこで終わるとみなされます (それ以外の場合は次の行が読み込まれます)。行をまたいで式を継続させるには、括弧で囲むのが一つの方法です。
- Julia 配列は列優先 (Fortran と同じ順序) ですが、NumPy の配列はデフォルトで行優先 (C と同じ順序) です。配列に対するループで最適な性能を達成するには、Julia と NumPy でループの順番を逆転させてください (参照: 配列にはメモリに沿った列優先の順序でアクセスする)。
- NumPy とは異なり、Julia の更新演算子 (
+=
,-=
, ...) はインプレースではありません。例えばA = [1, 1]; B = A; B += [3, 3]
としてもA
の値は変更されず、B
にはB + 3
の結果 (新しく構築される配列) が束縛されます。インプレースの操作を行うにはB .+= 3
とするか (参照: ドット構文)、明示的なループを使うか、InplaceOps.jl を使ってください。 - Python では関数の引数に対するデフォルト値の評価は関数が定義されるときに一度だけ行われますが、Julia ではメソッドが呼び出されるたびにデフォルト値が評価されます。例えば、Julia の関数
f(x=rand())= x
は引数を付けずに呼び出されるたびに新しい乱数を返します。また Julia でg(x=[1,2]) = push!(x,3)
を引数無しにg()
と呼び出すと、必ず[1,2,3]
が返ります。 - Python の
%
はモジュロ演算子ですが、Julia の%
は剰余演算子です。 - Python の
int
型は任意精度の整数を表しますが、Julia でよく使われるInt
型はマシンの整数型 (Int32
またはInt64
) に対応します。これは Julia ではInt
がオーバフローを起こし、例えば2^64 == 0
となることを意味します。これよりも大きな値が必要な場合には、Int128
やBigInt
あるいは浮動小数点数型 (Float64
) といった適切な型を使ってください。 - Julia では虚数単位
sqrt(-1)
をim
と表します。Python のj
は使えません。 - Julia の指数演算子は
^
であり、Python の**
とは異なります。 - Python はヌル値を
NoneType
型の値None
で表しますが、Julia はNothing
型の値nothing
で表します。 - Julia では二つの行列型に対する標準演算子が行列演算を表しますが、Python では要素ごとの演算を表します。例えば行列
A
,B
に対するA * B
は Julia で行列の乗算を表しますが、Python では要素ごとの乗算を表します。Julia のA * B
は Python のA @ B
と等価であり、Python のA * B
は Julia のA .* B
と等価です。 - Julia の随伴演算子
'
をベクトルに対して作用させるとベクトルの随伴 (行ベクトルの遅延表現) が返りますが、Python の転置演算子.T
はベクトルに対して作用させるとベクトルがそのまま返ります (つまり何もしません)。 - Julia では関数が (メソッドと呼ばれる) 具象実装をいくつも持つことができ、関数を呼び出すと多重ディスパッチでメソッドが選択されます。これに対して Python の関数は実装を一つしか持てません (多相的ではありません)。
- Julia にクラスは存在しません。存在するのは (可変または不変の) 構造体であり、データは持てますがメソッドは持てません。
- Python におけるクラスが持つメソッドの呼び出し
a = MyClass(x); x.func(y)
は、Julia の関数呼び出しa = MyStruct(x); func(x::MyStruct, y)
に対応します。一般に、Julia の多重ディスパッチは 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 では通常のワークフローで例外処理を使うことは推奨されません。理由は性能です。 - Julia のループは高速であり、性能のためにコードを「ベクトル化」する必要はありません。
- Julia で定数でないグローバル変数を使うときは注意が必要です。特に性能が要求されるループで使ってはいけません。Julia では (Python と異なり) 低レベルなコードを書けるので、グローバル変数による影響は非常に大きくなります (参照: パフォーマンス Tips)。
- Python では真偽値が要求される文脈で大部分の値を利用できます。例えば
if "a":
に続くブロックは実行され、if "":
に続くブロックは実行されません。Julia では、このような場合にBool
型への明示的な変換が必要です。Julia で空文字列を検出したいなら、if !isempty("")
と書くことになります。 - Julia ではループや
try
—catch
—finally
を含む大部分のコードブロックが新しいローカルブロックを作成します。Python と Julia の両方において、内包表記 (リストやジェネレータ) は新しいローカルスコープを作成し、if
は作成しません。
C/C++ との主な違い
- Julia の配列は角括弧を使ってアクセスし、そのとき
A[i,j]
のように複数の次元を指定できます。C/C++ の添え字アクセスはポインタやアドレスに対する参照を表す糖衣構文ですが、Julia はそうなっていません。詳細は添え字アクセス構文のドキュメントを参照してください。 - Julia では、配列や文字列などの添え字が 0 ではなく 1 から始まります。
- Julia の配列は他の変数に代入されてもコピーされません。
A = B
とした後にB
の要素を変更すると、A
の要素も変更されます。 +=
などの更新演算子はインプレースではありません。例えばA += B
はA = A + B
と等価であり、左辺の変数を右辺の評価結果に再束縛します。- Julia の配列は列優先 (Fortran と同じ順序) ですが、C/C++ の配列はデフォルトで行優先です。配列に対するループで最適な性能を達成するには、Julia と C/C++ でループの順番を逆転させてください (参照: 配列にはメモリに沿った列優先の順序でアクセスする)。
- Julia では、変数に対する代入や関数に対する引数の受け渡しで値はコピーされません。例えば関数が引数として受け取った配列を変更すれば、その変更は呼び出し側に伝わります。
- C/C++ と異なり、Julia では空白が意味を持ちます。そのため Julia プログラムから空白を追加/削除するときは注意が必要です。
-
Julia では、
42
のような小数点を持たない数値リテラルは基本的にInt
型の符号付き整数を作成します。リテラルが表す整数がマシンのワードサイズに収まらないときは大きな型への昇格が自動的に行われます。昇格先はInt64
(Int
がInt32
のとき) やInt128
もしくは任意精度整数BigInt
といった型です。数値リテラルのサイズや符号を指定するL
,LL
,U
,UL
,ULL
のような記号は存在しません。十進リテラルは常に符号付きで、十六進リテラルは必ず符号無しです (十六進リテラルは C/C++ と同様に0x
で始まります)。C/C++/Java および Julia の十進リテラルと異なり、十六進リテラルの型はその長さから決まります。リテラルの長さには最初についている
0
も含まれ、例えば0x0
と0x00
はUInt8
型であり、0x000
と0x0000
はUInt16
型となります。その後は十六進で五桁から八桁のリテラルがUInt32
型、九桁から十六桁のリテラルがUInt64
型、十七桁から三十二桁のリテラルがUInt128
型となります。この仕様は十六進リテラルでマスクを定義するときに意識する必要があります。例えば~0xf == 0xf0
と~0x000f == 0xfff0
は異なる値です。0o
が先頭に付く八進リテラルと0b
が先頭に付く二進リテラルも符号無しと扱われます。64 ビットの
Float64
のリテラルと 32 ビットのFloat32
のリテラルはそれぞれ1.0
および1.0f0
のように書きます。浮動小数点数のリテラルが与えられた値を正確に表現できない場合には丸めが行われ、BigFloat
への昇格は行われません。 - 文字列リテラルは
"
または"""
で囲んで表します。"""
を使う場合には、リテラルの中に"
を\"
とクオートすることなく入れられます。文字列リテラルでは変数の値や式を$variablename
および$(expression)
で補間でき、この部分はリテラルがある関数の文脈における変数名あるいは式の評価結果で置き換わります。 -
//
はRational
型の有理数を表します。C/C++ で//
は一行コメントですが、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++ と優先度が異なるので、括弧が必要になる場合があります。 - C/C++ の
^
はビット単位の XOR 演算を表しますが、Julia の^
はべき乗演算 (pow
) を表します。XOR 演算は Julia で⊻
またはxor
で表されます。 - C/C++ では
>>
の意味がシフトされる値の型によって変わります。これに対して Julia には>>
と>>>
という二つの右シフト演算子があり、>>>
は必ず算術右シフトを行い、>>
は必ず論理右シフトを行います。 - Julia の
->
は無名関数を作ります。ポインタを通じたメンバーアクセスではありません。 - Julia は
if
文やfor
/while
ループで括弧を必要としません。for (int i=1; i <= 3; i++)
ではなくfor i in [1, 2, 3]
と書き、if (i == 1)
ではなくif i == 1
と書いてください。 - Julia は数値の
0
や1
を真偽値として扱いません。Julia のif
が受け付けるのは真偽値だけなので、if (1)
と書くことはできません。if true
,if Bool(1)
,if 1==1
などと書く必要があります。 - Julia は条件分岐ブロック (
if
など) ・ループブロック (while
とfor
)・関数 (function
) の終端をend
で表します。C/C++ のif ( cond ) statement
というワンライナーに対応する Julia の文はif cond; statement; end
,cond && statement
,!cond || statement
です。最後の二つの書き方で代入文を使うときはcond && (x = value)
のように括弧で囲む必要があります。 - Julia には行を継続する構文がありません。もし行末までの入力が完全な式となるなら、その式はそこで終わるとみなされます (それ以外の場合は次の行が読み込まれます)。行をまたいで式を継続させるには、括弧で囲むのが一つの方法です。
- Julia のマクロはプログラムのテキストではなくパースされた式に対して動作するので、Julia コードの高度な変形が可能です。Julia のマクロは
@
で始まる名前を持ち、関数風の構文@mymacro(arg1, arg2, arg3)
と文風の構文@mymacro arg1 arg2 arg3
を持ちます。二つの構文は交換可能ですが、関数風の構文の方が他の式中に表れたときに使いやすく、意味が明確です。文風の構文はブロックに注釈をつけるのに使われることが多く、例えば@distributed for i in 1:n; #= body =#; end
のように使われます。マクロの引数がどこで終わるかが分かりにくいときは関数風の形式を使ってください。 - Julia はマクロ
@enum(name, value1, value2, ...)
を通して列挙型をサポートします。例えば@enum(Fruit, banana=1, apple, pear)
として使います。 - 慣習により、引数を変更する Julia の関数は名前の最後に
!
が付きます。push!
がこの例です。 - C++ はデフォルトで静的なディスパッチを行います。言い換えると、動的なディスパッチを行うには関数に
virtual
が必要です。これに対して、Julia では全てのメソッドがvirtual
です。さらに Julia はthis
だけではなく全ての引数の型を使って最も特定的なメソッドを選択して関数をディスパッチするので、C++ より一般的なディスパッチが可能です。
Common Lisp との主な違い
-
Julia の配列はデフォルトで 1 始まりの添え字を使います。添え字のオフセットを任意に設定することもできます。
-
関数と変数は同じ名前空間を共有します ("Lisp-1" です)。
-
Julia は
Pair
型を持ちますが、これはCOMMON-LISP:CONS
のようには使われません。Julia が持つ様々な反復可能コレクション (可変長引数やタプルなど) はたいていの場合で交換可能な形で使うことができます。Tuple
は Common Lisp のリストに最も近いコレクションであり、異種の要素からなる短いコレクションを表します。連想リストを使う場面ではNamedTuple
を使ってください。同種の型の大きなコレクションに対してはArray
やDict
を使うべきです。 -
Julia を使って行うプロトタイピングの典型的なワークフローでは、イメージ (ソースコード) の操作が反復的に行われます。これは Revise.jl パッケージで実装されます。
-
Common Lisp の
Bignum
にあたる型は Julia でサポートされますが、変換は自動的ではありません。通常の整数はオーバーフローします。 -
モジュール (名前空間) は階層的にできます。
import
とusing
には二つの役割があります: コードを読み込むことと、名前空間で識別子を利用可能にすることです。import
はモジュールの名前だけをインポートします (ASDF:LOAD-OP
とほぼ同じです)。スロット名を個別にエクスポートする必要はありません。モジュールのグローバル変数はモジュールの外側から変更できません (どうしてもと言うときの脱出ハッチとしてeval(mod, :(var = val))
があります)。 -
Julia のマクロは
@
で始まる名前を持ちます。 -
Julia のマクロは Common Lisp ほど滑らかには言語に統合されていないので、Common Lisp に比べると使われる頻度は落ちます。
-
Julia のマクロは健全性を持ちます。
-
表面的な構文の違いにより、
COMMON-LISP:&BODY
に対応するものが Julia にはありません。 -
Julia の関数は全て汎用であり、多重ディスパッチを使います。引数リストが同じテンプレートに従う必要はなく、これにより強力なイディオムも可能になっています (参照:
do
ブロック構文)。省略可能引数とキーワード引数は異なるものとして扱われます。メソッドの曖昧性は Common Lisp Object System のようには解決されず、曖昧性が起こった引数の組に対するより特定的なメソッドの定義が必要です。 -
シンボルはパッケージに属さず、それ自体には他の値を含みません。
M.var
はモジュールM
でシンボルvar
を評価します。 -
Julia は クロージャを含む関数的プログラミングのスタイルを完全にサポートします。ただし、関数的プログラミングのスタイルが Julia らしい問題解決法であるとは限りません。例えばクロージャがキャプチャした変数を変更するときは、性能のためにワークアラウンドが必要になる可能性があります。