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.@nloopsMacro
@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 للتعبير السابق. باستخدام الأقواس والفواصل المنقوطة، يمكنك تقديم تعبيرات متعددة البيانات.

source
Base.Cartesian.@nrefMacro
@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])
source
Base.Cartesian.@nextractMacro
@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]
source
Base.Cartesian.@nexprsMacro
@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
source
Base.Cartesian.@ncallMacro
@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])
source
Base.Cartesian.@ncallkwMacro
@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
source
Base.Cartesian.@ntupleMacro
@ntuple N expr

ينشئ N-tuple. @ntuple 2 i سينشئ (i_1, i_2)، و @ntuple 2 k->k+1 سينشئ (2,3).

source
Base.Cartesian.@nallMacro
@nall N expr

تحقق مما إذا كانت جميع التعبيرات التي تم إنشاؤها بواسطة تعبير الدالة المجهولة expr تقيم إلى true.

@nall 3 d->(i_d > 1) ستولد التعبير (i_1 > 1 && i_2 > 1 && i_3 > 1). يمكن أن يكون هذا مفيدًا للتحقق من الحدود.

source
Base.Cartesian.@nanyMacro
@nany N expr

تحقق مما إذا كانت أي من التعبيرات التي تم إنشاؤها بواسطة تعبير الدالة المجهولة expr تقيم إلى true.

@nany 3 d->(i_d > 1) ستولد التعبير (i_1 > 1 || i_2 > 1 || i_3 > 1).

source
Base.Cartesian.@nifMacro
@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
source