يُصرّف HotXLS الصيغ المكتوبة بترميز المراجع R1C1 عبر TXLSCalculator.GetCompiledFormulaR1C1، الذي يقبل أشكالًا مثل R2C3 (مطلق) وR[-1]C[2] (إزاحة نسبية) وRC (الخلية الحالية)، ويحوّلها إلى ترميز A1 بالنسبة إلى صف وعمود الخلية التي تقيم فيها الصيغة، ثم يمرّر الناتج إلى المُصرِّف نفسه الذي يعالج صيغ A1 العادية. وبالنسبة إلى كود Delphi وC++Builder الذي يولّد الصيغة نفسها عبر مئات الصفوف، تُزيل هذه الطريقة الواحدة فئة كاملة من أخطاء تسلسل السلاسل النصية
وهذه الفئة من الأخطاء مألوفة لكل من ملأ عمودًا برمجيًا. فأنت تكرّر عبر الصفوف، وتبني لكل صف سلسلة صيغة A1 باستخدام Format('D%d*E%d', [Row, Row]). وكل تكرار يحشر أرقام الصفوف في النص، وأرقام الصفوف هي الجزء الوحيد الذي يتغيّر. أخطئ في الإزاحة مرة واحدة، أو اخلط عدّاد حلقة يبدأ من 0 بأرقام صفوف A1 التي تبدأ من 1، أو أزح كتلة البيانات بصف ترويسة، فتشير كل صيغة في العمود إلى صف خاطئ بمقدار واحد. ولا يُرفع أي استثناء؛ الأرقام خاطئة فحسب. أما الصيغة التي قصدتها فعلًا، "اضرب الخليتين الواقعتين على يساري"، فلا تذكر رقم صف على الإطلاق، وترميز R1C1 يتيح لك كتابتها بهذه الطريقة
ما هو ترميز R1C1 ومتى ينبغي استخدامه؟
يخاطب ترميز R1C1 الخلايا برقم الصف ورقم العمود بدلًا من حرف العمود مع رقم الصف، ويُعلّم المراجع النسبية كإزاحات صريحة من خلية الصيغة. فـ R2C3 هي الخلية المطلقة في الصف 2 والعمود 3، التي يكتبها A1 على هيئة $C$2. وR[-1]C[2] تعني صفًا واحدًا إلى الأعلى وعمودين إلى اليمين من أي موضع تقع فيه الصيغة. وRC هي خلية الصيغة نفسها. والإزاحات الموضوعة بين الأقواس المعقوفة هي بيت القصيد: فالمرجع النسبي في R1C1 يُقرأ بالطريقة نفسها أيًّا كانت الخلية التي تستضيفه، في حين يتغيّر تمثيل A1 للمرجع نفسه مع كل صف
ويستحق هذا الترميز مكانه في سيناريو واحد بالضبط، وهو سيناريو شائع: توليد القوالب، حيث يجب زرع الصيغة النسبية نفسها في كل صف من منطقة بيانات. ففي A1 يجب عليك إعادة توليد نص الصيغة لكل صف. أما في R1C1 فالنص ثابت. وهذا أقرب أيضًا إلى الطريقة التي تفكّر بها صيغ ملفات جداول البيانات داخليًا: فسجلات الصيغ المشتركة تخزّن المراجع النسبية كإزاحات صف وعمود من الخلية المضيفة، ولذا فإن سلسلة A1 الخاصة بكل صف شيء يركّبه كودك لمجرد أن يفكّكه المحلّل من جديد إلى إزاحات. ويتخطّى R1C1 هذه الرحلة ذهابًا وإيابًا. أما الصيغ التفاعلية التي يكتبها البشر فيبقى A1 الخيار الطبيعي لها، ولهذا يظل الخيار الافتراضي في كل موضع من HotXLS
كيف يُصرّف HotXLS صيغة R1C1؟
يتيح HotXLS هذه الميزة على مستويين، وقد أُضيفت في الإصدار v2.175.0. فـ TXLSCalculator.GetCompiledFormulaR1C1(UncompiledFormula: String; SheetID, CurRow, CurCol: Integer): TXLSCompiledFormula هي التي يستدعيها معظم الكود: تُعيد صيغة مُصرَّفة جاهزة للتقييم، تمامًا مثل شقيقتها GetCompiledFormula الخاصة بـ A1، لكن مع معاملين إضافيين يحدّدان الصف والعمود المبنيين على 0 للخلية التي تنتمي إليها الصيغة. وتحتها تُنتج TXLSFormula.GetCompiledR1C1 شجرة البناء الخام، وتؤدي دالة مستقلة R1C1ToA1(const AFormula: String; CurRow, CurCol: Integer): String تحويل الترميز الفعلي. وخط المعالجة بسيط عن قصد: ترجمة نص R1C1 إلى نص A1 مكافئ باستخدام إحداثيات الخلية المضيفة، ثم تصريف نص A1 عبر المحرك القائم — المحرك نفسه الذي يحلّ الأسماء المعرَّفة والمراجع بين الأوراق ويوزّع دوال أوراق العمل المخصصة
ولأن التحويل يحدث قبل التصريف، يتصرّف كل ما بعده كما لو كنت قد كتبت صيغة A1 بنفسك. فـ R1C1ToA1('R2C3', 4, 3) تُعيد '$C$2' بصرف النظر عن الخلية المضيفة، لأن كلا الإحداثيين مطلق. وR1C1ToA1('SUM(R[-3]C[0]:R[-1]C[0])', 4, 3) — وهي صيغة تقيم في D5، لأن CurRow = 4 وCurCol = 3 مبنيان على 0 — تُعيد 'SUM(D2:D4)': فقد حُلّت الإزاحات بالنسبة إلى الصف 5 والعمود D، وصدرت كمراجع A1 نسبية عادية. ولا تحتاج النطاقات إلى معالجة خاصة؛ فالنقطتان تمرّان كما هما ويتحوّل كل طرف على حدة
// الصيغة تقيم في D5: CurRow = 4 وCurCol = 3 (كلاهما مبني على 0)
S := R1C1ToA1('R2C3', 4, 3);
// S = '$C$2' (صف وعمود مطلقان)
S := R1C1ToA1('SUM(R[-3]C[0]:R[-1]C[0])', 4, 3);
// S = 'SUM(D2:D4)' (الإزاحات محلولة بالنسبة إلى D5)
S := R1C1ToA1('ROUND(R[-1]C[0], 2)', 4, 3);
// S = 'ROUND(D4, 2)' (حرف R في ROUND يُترك كما هو)
ملء عمود بصيغة نسبية واحدة
وتظهر الفائدة في الحلقة. قارن نسخة A1، التي تعيد توليد نص الصيغة في كل تكرار، بنسخة R1C1، حيث الصيغة ثابتة ولا يتحرّك سوى إحداثيات الخلية المضيفة. وكلتاهما تُصرَّف عبر TXLSCalculator وتُقيَّم بـ GetValue؛ ويتلقّى الحاسب استدعاءً خلفيًا لمزوّد الخلايا عند إنشائه كي يستطيع المُقيِّم سحب قيم الخلايا من مصدر بياناتك
// أسلوب A1: سلسلة صيغة مختلفة لكل صف
for Row := 1 to 500 do
begin
FormulaText := Format('D%d*E%d', [Row + 1, Row + 1]); // صفوف A1 مبنية على 1
Compiled := Calc.GetCompiledFormula(FormulaText, 0);
// ... تقييم، تخزين، تحرير ...
end;
const
AmountFormula = 'RC[-2]*RC[-1]'; // خليتان إلى اليسار، في الصف نفسه
var
Calc: TXLSCalculator;
Compiled: TXLSCompiledFormula;
Value: Variant;
Row: Integer;
begin
Calc := TXLSCalculator.Create(nil, Provider.GetValue);
try
for Row := 1 to 500 do
begin
Compiled := Calc.GetCompiledFormulaR1C1(AmountFormula, 0, Row, 5);
try
if Calc.GetValue(0, Compiled, Row, 5, Value, 1) = lxOk then
StoreResult(Row, 5, Value);
finally
Compiled.Free;
end;
end;
finally
Calc.Free;
end;
end;
لا تحتوي حلقة R1C1 على أي حساب للصفوف في نص الصيغة إطلاقًا. فـ 'RC[-2]*RC[-1]' تعني "الصف نفسه، عمودان إلى اليسار، مضروبًا في الصف نفسه، عمود واحد إلى اليسار" في الصف 2 وفي الصف 500 على حد سواء، وإذا انتقلت كتلة البيانات لاحقًا إلى الأسفل بصف ترويسة، فلا يتغيّر ثابت الصيغة — بل حدود الحلقة فقط. ففي نسخة A1 موضعان يمكن أن تُخطئ فيهما تعديل + 1؛ أما في نسخة R1C1 فلا موضع على الإطلاق
ما أشكال R1C1 التي يقبلها المحوّل؟
يتعرّف المحوّل R1C1ToA1 على الأشكال الموثَّقة للإصدار v2.175.0: R[n]C[m] للإزاحات النسبية في أي اتجاه، وRnCm للصف والعمود المطلقين، وR[-n]C[m] بإزاحات سالبة، وRC المجرّدة لخلية الصيغة نفسها، والنطاقات مثل R1C1:R3C3 أو R[-1]C:R[1]C. ويُحلَّل جزءا الصف والعمود على حدة، ولذا تعمل الأشكال المختلطة مثل R[1]C3 — صف نسبي وعمود مطلق — أيضًا، والحروف غير حساسة لحالة الأحرف، فتُصرَّف r[-1]c[2] كما تُصرَّف توأمها بالأحرف الكبيرة. وتصبح الأجزاء الموضوعة بين أقواس معقوفة إحداثيات A1 غير مثبَّتة (نسبية)؛ بينما تصبح الأرقام المجرّدة إحداثيات مطلقة مثبَّتة بعلامة $
والسؤال المثير هو كيف يتجنّب المحوّل تشويه كل ما عدا ذلك في الصيغة، لأن R وC حرفان شائعان. فقاعدة التمييز لديه مبنية على الرموز: لا يُعدّ حرف R بداية مرجع إلا حين لا يسبقه حرف آخر، ويجب حينها أن يُحلَّل المرشّح بالكامل — جزء صف اختياري، ثم C إلزامي، ثم جزء عمود اختياري — وإلا استُعيد النص دون مساس. ولهذا تحوّل ROUND(R[-1]C[0], 2) المرجع الداخلي فقط: فحرف R في ROUND يتبعه O لا رقم ولا قوس معقوف ولا C، فيفشل التحليل ويمرّ اسم الدالة حرفيًا. ويحمي المنطق نفسه ROW()، أما أسماء الدوال التي تبدأ بـ C فليست مرشّحة أبدًا، لأن R وحده يبدأ المرجع. وتذكر ملاحظات إصدار v2.175.0 أن السلاسل الحرفية والمعرّفات تُتخطّى أيضًا؛ ومع ذلك، إذا صادف أن احتوت سلسلة حرفية مقتبسة في صيغتك نصًا بشكل مرجع R1C1 تمامًا، فمن المجدي فحص الناتج المُصرَّف مرة واحدة قبل الوثوق به في الإنتاج
أسس الإحداثيات وتفاصيل التثبيت الجديرة بالمعرفة
يلتقي عرفان داخل هذه الواجهة البرمجية، والتمييز بينهما يجنّبك الفخ الحقيقي الوحيد. فالمعاملان CurRow وCurCol في GetCompiledFormulaR1C1 مبنيان على 0، اتباعًا لواجهة الحاسب البرمجية، بينما الأرقام داخل الترميز نفسه مبنية على 1، مطابقةً لما يعرضه Excel: فـ R2C3 هي الصف 2 والعمود 3، أي $C$2 لا $D$3. وإذا كان عدّاد حلقتك مبنيًا على 0 أصلًا فمرّره مباشرة بوصفه CurRow؛ فالتعديل + 1 يقيم داخل المحوّل لا في كودك
وثمة تفصيل آخر يهم إذا كانت الصيغة المُصرَّفة ستعمّر أطول من الخلية التي صُرِّفت لأجلها. فحين تحذف جزء الصف أو العمود — الشكلان RC[-1] أو R[2]C — يُحلّ الإحداثي المحذوف إلى الخلية المضيفة ويصدر كإحداثي مطلق مثبَّت بعلامة $ في نص A1 المحوَّل. وعند التقييم لا يظهر هذا، لأن القيمة واحدة في الحالتين. لكن التثبيت النسبي مقابل المطلق يقرّر كيف تنزاح المراجع حين تُدرَج صفوف أو أعمدة أو تُحذف لاحقًا، كما هو مشروح في المقال المرافق عن تعديل مراجع الصيغ. وإذا احتجت إلى أن يبقى إحداثي نسبيًا عبر التعديلات البنيوية، فاكتب الإزاحة صراحةً — R[0]C[-1] بدلًا من RC[-1] — كي يُصدر المحوّل مرجع A1 غير مثبَّت
تصريف R1C1 جزء من محرك الصيغ في مكوّن HotXLS لـ Excel في Delphi، إلى جانب مُصرِّف A1 ورسم إعادة الحساب البياني وواجهة التقييم البرمجية المبيّنة أعلاه. وإذا كان كودك يبني جداول البيانات بتكرار الصيغ نزولًا عبر الأعمدة، فإن نقل تلك الحلقات من A1 المركَّب من سلاسل إلى ثابت R1C1 واحد هو من أرخص ترقيات الموثوقية المتاحة