مقالة تقنية

ترميز صيغ R1C1 في دلفي باستخدام HotXLS

يقوم HotXLS بتجميع الصيغ المكتوبة بترميز مراجع R1C1 من خلال TXLSCalculator.GetCompiledFormulaR1C1، والتي تقبل أشكالاً مثل R2C3 (مطلق)، وR[-1]C[2] (إزاحة نسبية)، وRC (الخلية الحالية)، وتحولها إلى ترميز A1 مقابل صف وعمود الخلية التي توجد بها الصيغة، وتغذي النتيجة لنفس المترجم الذي يتعامل مع صيغ A1 العادية. بالنسبة لكود دلفي و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 هو الاستدعاء الذي يجريه معظم الكود: يعيد صيغة مجمعة جاهزة للتقييم، تماماً مثل شقيقته في A1 وهي GetCompiledFormula، ولكن مع معاملين إضافيين يحددان الصف والعمود المعتمدين على 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 نسبية بسيطة. ولا تحتاج النطاقات إلى معالجة خاصة؛ حيث تمر النقطتان وتتحول كل نقطة نهاية بشكل مستقل

// The formula lives in D5: CurRow = 4, CurCol = 3 (both 0-based)
S := R1C1ToA1('R2C3', 4, 3);
// S = '$C$2'  (absolute row and column)

S := R1C1ToA1('SUM(R[-3]C[0]:R[-1]C[0])', 4, 3);
// S = 'SUM(D2:D4)'  (offsets resolved against D5)

S := R1C1ToA1('ROUND(R[-1]C[0], 2)', 4, 3);
// S = 'ROUND(D4, 2)'  (the R in ROUND is left alone)

ملء عمود بصيغة نسبية واحدة

تظهر الفائدة في الحلقة. قارن إصدار A1، الذي يعيد إخراج نص الصيغة في كل تكرار، بإصدار R1C1، حيث تكون الصيغة ثابتاً وتتحرك إحداثيات المضيف فقط. كلاهما يتجمع من خلال TXLSCalculator ويقيم بـ GetValue؛ ويأخذ الحاسب رد اتصال مزود الخلايا عند البناء حتى يتمكن المقيم من سحب قيم الخلايا من مصدر بياناتك

// A1 style: a different formula string for every row
for Row := 1 to 500 do
begin
  FormulaText := Format('D%d*E%d', [Row + 1, Row + 1]);  // 1-based A1 rows
  Compiled := Calc.GetCompiledFormula(FormulaText, 0);
  // ... evaluate, store, free ...
end;
const
  AmountFormula = 'RC[-2]*RC[-1]';  // two cells to the left, same row
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 semantics لخلية الصيغة نفسها، والنطاقات مثل 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 Delphi Excel Component، إلى جانب مترجم A1، ورسم بياني لإعادة الحساب، وواجهة برمجة تطبيقات التقييم الموضحة أعلاه. إذا كان كودك يبني جداول البيانات عن طريق تكرار الصيغ أسفل الأعمدة، فإن نقل هذه الحلقات من A1 المربوط بسلاسل نصية إلى ثاب R1C1 فردي هو أحد أرخص ترقيات الموثوقية المتاحة