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
بشكل عام، يسمح لك كارتسيان بكتابة كود عام يحتوي على عناصر متكررة، مثل الحلقات المتداخلة في هذا المثال. تشمل التطبيقات الأخرى التعبيرات المتكررة (مثل فك الحلقات) أو إنشاء استدعاءات دالة بعدد متغير من الوسائط دون استخدام بناء "سلات" (i...
).
Basic syntax
الصيغة (الأساسية) لـ @nloops
هي كما يلي:
- يجب أن تكون الحجة الأولى عددًا صحيحًا (ليس متغيرًا) يحدد عدد الحلقات.
- الحجة الثانية هي رمز البادئة المستخدمة لمتغير التكرار. هنا استخدمنا
i
، وتم إنشاء المتغيراتi_1، i_2، i_3
. - الحجة الثالثة تحدد النطاق لكل متغير من متغيرات التكرار. إذا استخدمت متغيرًا (رمزًا) هنا، يتم اعتباره كـ
axes(A, dim)
. بشكل أكثر مرونة، يمكنك استخدام بناء تعبير الدالة المجهولة الموضح أدناه. - الحجة الأخيرة هي جسم الحلقة. هنا، هذا ما يظهر بين
begin...end
.
هناك بعض الميزات الإضافية لـ @nloops
الموضحة في reference section.
@nref
يتبع نمطًا مشابهًا، حيث يولد A[i_1,i_2,i_3]
من @nref 3 A i
. الممارسة العامة هي القراءة من اليسار إلى اليمين، ولهذا السبب @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
.
اختياريًا، يمكنك تقديم تعبيرات "قبل" و "بعد". يتم تنفيذ هذه التعبيرات أولاً وآخرًا، على التوالي، في جسم كل حلقة. على سبيل المثال:
@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
قم بإنشاء متغيرات 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
-tuple. @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("Dimension ", d, " too big")) d->println("All OK")
سيولد:
if i_1 > size(A, 1)
error("Dimension ", 1, " too big")
elseif i_2 > size(A, 2)
error("Dimension ", 2, " too big")
else
println("All OK")
end