Base.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의 (기본) 구문은 다음과 같습니다:
- 첫 번째 인수는 루프의 수를 지정하는 정수(*변수가 아님)여야 합니다.
- 두 번째 인자는 반복자 변수에 사용되는 기호 접두사입니다. 여기서는
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 bodyexprN 개의 중첩 루프를 생성하며, itersym을 반복 변수의 접두사로 사용합니다. rangeexpr는 익명 함수 표현식일 수도 있고, 간단한 기호 var일 경우에는 범위가 차원 d에 대해 axes(var, 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단지 post-expression만 원한다면, pre-expression에 nothing를 공급하십시오. 괄호와 세미콜론을 사용하여 다중 문장 표현식을 제공할 수 있습니다.
Base.Cartesian.@nref — Macro@nref N A indexexprA[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 isymN 개의 변수를 생성하여 esym_1, esym_2, ..., esym_N로 isym에서 값을 추출합니다. 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 exprN 개의 표현식을 생성합니다. 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]
endBase.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 exprN-튜플을 생성합니다. @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 조건식 expr
@nif N 조건식 expr elseexprif ... elseif ... else ... end 문장의 시퀀스를 생성합니다. 예를 들어:
@nif 3 d->(i_d >= size(A,d)) d->(error("차원 ", d, " 너무 큼")) d->println("모두 OK")다음과 같이 생성됩니다:
if i_1 > size(A, 1)
error("차원 ", 1, " 너무 큼")
elseif i_2 > size(A, 2)
error("차원 ", 2, " 너무 큼")
else
println("모두 OK")
end