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 bodyexpr
N
개의 중첩 루프를 생성하며, 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 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
N
개의 변수를 생성하여 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 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 조건식 expr
@nif N 조건식 expr elseexpr
if ... 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