Base.Cartesian
非エクスポートのCartesianモジュールは、多次元アルゴリズムを書くのを容易にするマクロを提供します。ほとんどの場合、straightforward techniquesを使ってそのようなアルゴリズムを書くことができます。ただし、Base.Cartesian
が依然として便利または必要な場合がいくつかあります。
Principles of usage
使用の簡単な例は次のとおりです:
@nloops 3 i A begin
s += @nref 3 A i
end
次のコードを生成します:
for i_3 = axes(A, 3)
for i_2 = axes(A, 2)
for i_1 = axes(A, 1)
s += A[i_1, i_2, i_3]
end
end
end
一般的に、Cartesianは、ネストされたループのような繰り返し要素を含む汎用コードを書くことを可能にします。他の用途には、繰り返しの式(例:ループの展開)や、「スプラット」構文(i...
)を使用せずに可変数の引数を持つ関数呼び出しを作成することが含まれます。
Basic syntax
@nloops
の(基本的な)構文は次のとおりです:
- 最初の引数は整数でなければならず(変数ではなく)、ループの回数を指定します。
- 2番目の引数は、イテレータ変数に使用されるシンボルプレフィックスです。ここでは
i
を使用し、変数i_1, i_2, i_3
が生成されました。 - 第三の引数は、各イテレータ変数の範囲を指定します。ここで変数(シンボル)を使用すると、それは
axes(A, dim)
として扱われます。より柔軟に、以下に説明する匿名関数式の構文を使用することができます。 - ループの最後の引数は、ループの本体です。ここでは、それが
begin...end
の間に表示されます。
@nloops
の追加機能については、reference sectionに記載されています。
@nref
は似たようなパターンに従い、@nref 3 A i
からA[i_1,i_2,i_3]
を生成します。一般的な慣習は左から右に読むことであり、これが理由で@nloops
は@nloops 3 i A expr
となります(for i_2 = axes(A, 2)
のように、i_2
は左側にあり、範囲は右側にあります)に対し、@nref
は@nref 3 A i
となります(A[i_1,i_2,i_3]
のように、配列が最初に来ます)。
Cartesianでコードを開発している場合、生成されたコードを調べることでデバッグが容易になることがあります。その際は@macroexpand
を使用してください:
julia> @macroexpand @nref 2 A i
:(A[i_1, i_2])
Supplying the number of expressions
これらのマクロの最初の引数は式の数であり、整数でなければなりません。複数の次元で機能することを意図した関数を書くとき、これはハードコーディングしたくないものかもしれません。推奨されるアプローチは、@generated function
を使用することです。以下はその例です:
@generated function mysum(A::Array{T,N}) where {T,N}
quote
s = zero(T)
@nloops $N i A begin
s += @nref $N A i
end
s
end
end
もちろん、quote
ブロックの前に式を準備したり、計算を行ったりすることもできます。
Anonymous-function expressions as macro arguments
おそらく Cartesian
の最も強力な機能の一つは、解析時に評価される無名関数式を提供できる能力です。簡単な例を考えてみましょう:
@nexprs 2 j->(i_j = 1)
@nexprs
は、パターンに従ったn
の式を生成します。このコードは次のステートメントを生成します:
i_1 = 1
i_2 = 1
各生成された文では、"孤立した" j
(匿名関数の変数)が範囲 1:2
の値に置き換えられます。一般的に言えば、CartesianはLaTeXのような構文を使用します。これにより、インデックス j
に対して数学的な操作を行うことができます。以下は、配列のストライドを計算する例です:
s_1 = 1
@nexprs 3 j->(s_{j+1} = s_j * size(A, j))
表現を生成します
s_1 = 1
s_2 = s_1 * size(A, 1)
s_3 = s_2 * size(A, 2)
s_4 = s_3 * size(A, 3)
匿名関数式は実際に多くの用途があります。
Macro reference
Base.Cartesian.@nloops
— Macro@nloops N itersym rangeexpr bodyexpr
@nloops N itersym rangeexpr preexpr bodyexpr
@nloops N itersym rangeexpr preexpr postexpr bodyexpr
N
個のネストされたループを生成します。itersym
をイテレーション変数のプレフィックスとして使用します。rangeexpr
は無名関数式であるか、単純なシンボル var
の場合、範囲は次のようになります: axes(var, d)
(次元 d
の場合)。
オプションで、「pre」と「post」の式を提供できます。これらはそれぞれ、各ループの本体の最初と最後に実行されます。例えば:
@nloops 2 i A d -> j_d = min(i_d, 5) begin
s += @nref 2 A j
end
は次のように生成されます:
for i_2 = axes(A, 2)
j_2 = min(i_2, 5)
for i_1 = axes(A, 1)
j_1 = min(i_1, 5)
s += A[j_1, j_2]
end
end
もしポスト式だけが必要な場合は、nothing
をプレ式として指定してください。括弧とセミコロンを使用することで、複数のステートメント式を提供できます。
Base.Cartesian.@nref
— Macro@nref N A indexexpr
A[i_1, i_2, ...]
のような式を生成します。indexexpr
は、イテレーションシンボルのプレフィックス、または無名関数の式であることができます。
例
julia> @macroexpand Base.Cartesian.@nref 3 A i
:(A[i_1, i_2, i_3])
Base.Cartesian.@nextract
— Macro@nextract N esym isym
isym
から値を抽出するためにesym_1
、esym_2
、...、esym_N
のN
個の変数を生成します。isym
はSymbol
または無名関数式のいずれかです。
@nextract 2 x y
は次のように生成されます。
x_1 = y[1]
x_2 = y[2]
一方、@nextract 3 x d->y[2d-1]
は次のようになります。
x_1 = y[1]
x_2 = y[3]
x_3 = y[5]
Base.Cartesian.@nexprs
— Macro@nexprs N expr
N
個の式を生成します。expr
は無名関数の式である必要があります。
例
julia> @macroexpand Base.Cartesian.@nexprs 4 i -> y[i] = A[i+j]
quote
y[1] = A[1 + j]
y[2] = A[2 + j]
y[3] = A[3 + j]
y[4] = A[4 + j]
end
Base.Cartesian.@ncall
— Macro@ncall N f sym...
関数呼び出し式を生成します。sym
は任意の数の関数引数を表し、最後の引数は無名関数式である可能性があり、N
個の引数に展開されます。
例えば、@ncall 3 func a
は次のように生成されます。
func(a_1, a_2, a_3)
一方、@ncall 2 func a b i->c[i]
は次のようになります。
func(a, b, c[1], c[2])
Base.Cartesian.@ncallkw
— Macro@ncallkw N f kw sym...
キーワード引数 kw...
を持つ関数呼び出し式を生成します。@ncall
の場合と同様に、sym
は任意の数の関数引数を表し、その最後は無名関数式である可能性があり、N
引数に展開されます。
例
julia> using Base.Cartesian
julia> f(x...; a, b = 1, c = 2, d = 3) = +(x..., a, b, c, d);
julia> x_1, x_2 = (-1, -2); b = 0; kw = (c = 0, d = 0);
julia> @ncallkw 2 f (; a = 0, b, kw...) x
-3
Base.Cartesian.@ntuple
— Macro@ntuple N expr
N
-タプルを生成します。@ntuple 2 i
は(i_1, i_2)
を生成し、@ntuple 2 k->k+1
は(2,3)
を生成します。
Base.Cartesian.@nall
— Macro@nall N expr
匿名関数式 expr
によって生成されたすべての式が true
に評価されるかどうかを確認します。
@nall 3 d->(i_d > 1)
は式 (i_1 > 1 && i_2 > 1 && i_3 > 1)
を生成します。これは境界チェックに便利です。
Base.Cartesian.@nany
— Macro@nany N expr
匿名関数式 expr
によって生成された式のいずれかが true
に評価されるかどうかを確認します。
@nany 3 d->(i_d > 1)
は式 (i_1 > 1 || i_2 > 1 || i_3 > 1)
を生成します。
Base.Cartesian.@nif
— Macro@nif N conditionexpr expr
@nif N conditionexpr expr elseexpr
if ... elseif ... else ... end
ステートメントのシーケンスを生成します。例えば:
@nif 3 d->(i_d >= size(A,d)) d->(error("次元 ", d, " が大きすぎます")) d->println("すべて正常")
は次のように生成されます:
if i_1 > size(A, 1)
error("次元 ", 1, " が大きすぎます")
elseif i_2 > size(A, 2)
error("次元 ", 2, " が大きすぎます")
else
println("すべて正常")
end