ضع =VLOOKUP(A1,B:B,1) في خلية في العمود B وسيحسبها Excel بلا اعتراض. وقدم المصنف نفسه إلى محرك إعادة حساب يعتمد رسم الاعتماديات فمن المرجح أن تحصل على خطأ مرجع دائري، لأن الصيغة تعتمد على نطاق يحوي الصيغة. أفاد HotXLS بذلك بالضبط حتى v2.361.98. والإصلاح ليس حالة خاصة للنطاقات كاملة الأعمدة؛ إنه تمييز بين نوعين من حافة اعتمادية يحتاجها محرك جداول بيانات ولا يملكه رسم موجه عادي
وسيط مصفوفة المزامحة في عائلة البحث، أي LOOKUP وMATCH وHLOOKUP وVLOOKUP وXLOOKUP وXMATCH، يوسم الآن كمرجع مزامحة. المرجع المزامح ما يزال يبذر الاتساخ، فتحرير خلية داخل النطاق يعيد حساب الصيغة، لكنه لا يسهم أبدا في كشف الدورات ولا في ترتيب التقويم. الدورات الحقيقية ما تزال تجد؛ والزائفة زالت
لماذا يجيز Excel لنطاق مزامحة أن يحوي الصيغة؟
لأن ذلك الوسيط لا يستهلك كما يستهلك معامل حسابي. تمسح عائلة البحث النطاق بحثا عن قيم مخزنة وتعيد مطابقة؛ وهي لا تشترط أن يكون النطاق قد قُوّم إلى اكتمال أولا. يعامل Excel نطاق مزامحة متداخلا مع ذاته كقراءة لما تحمله تلك الخلايا حاليا، وهو الدلالة نفسها التي يطبقها على أي مصنف غير تكراري: الخلايا التي لم يعاد حسابها في هذا المرور تسهم بقيمتها المحسوبة الأخيرة
المراجع كاملة الأعمدة تجعل هذه الحالة الاعتيادية لا الغريبة. فـ B:B هي الطريقة الاصطلاحية لكتابة «جدول المزامحة كله» في ورقة تلحق صفوف، وأي صيغة تعيش في العمود B تكون عندئذ داخل نطاق مزامحتها هي. النماذج المالية وأوراق التسوية ومصنفات التدقيق تفعل هذا باستمرار، عادة دون أن ينتبه أحد إلى أن النطاق متداخل
ماذا يفعل رسم الاعتماديات بالصيغة نفسها
يعيد HotXLS الحساب تزايديا، وهو ما يتطلب رسما حقيقيا للاعتماديات: عقدا للخلايا، وحوافا للمراجع، وترتيبا طوبولوجيا للتقويم، ومرور مكونات شديدة الاتصال لتصنيف الدورات. تلك الآلية موصوفة في مقال إعادة الحساب التزايدية، وهي بالضبط سبب ظهور الإيجابي الزائف
استخرج الاعتماديات من =VLOOKUP(A1,B:B,1) في الخلية B7 فيعطي الوسيط الثاني نطاقا يحوي B7 ذاتها. يصبح للرسم الآن حلقة ذاتية. لا تصل درجة دخول تلك العقدة إلى الصفر أبدا، فلا يستطيع المرور الطوبولوجي جدولتها أبدا، ويصنفها مرور المكونات كدورة. المحرك يستدل استدلالا صائبا على الرسم الذي أعطي له. والرسم هو النموذج الخاطئ، لأنه يرمز لنوع حافة واحد حيث لجدول البيانات نوعان
صنفان من الحواف في رسم واحد
يضيف التغيير علما إلى سجل المرجع المحلول، هو TXLSDepRange.LookupScan، يضبطه مستخرج الاعتماديات عندما يجوب وسيط مصفوفة المزامحة لإحدى الدوال الست. وفي اتجاه المصب، تخزن الحواف المنبثقة من تلك المراجع بعيدا عن الحواف العادية: تحتفظ عقدة الرسم بقائمتي ScanDependents وScanPrecedents إلى جانب قائمتيها العاديتين للتابعين والسوابق
الفصل هو ما يجعل الدلالة صائبة. تجوب حواف المزامحة بنشر الاتساخ، فالتحرير في أي مكان في B:B ما يزال يوسم B7 متسخا وتعيد B7 الحساب. ولا تعد حواف المزامحة أبدا في درجة الدخول ولا تدخل باني المكونات أبدا، فلا تستطيع صنع طريق مسدود طوبولوجي ولا تصنيفها دورة. وغيّر تنفيذ الرسمين في المكتبة معا، الرسم الكلاسيكي لكل مصنف والرسم مساحة عمل عبر المصنفات الذي يحمل تحليل المكونات؛ وتركهما ينحرفان سينتج مصنفا يعيد الحساب بشكل مختلف بحسب فتح وحده أم كجزء من مساحة عمل
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
begin
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Ledger');
Sheet.Cells[1, 1].Value := 'ACC-4471';
Sheet.Cells[1, 2].Value := 1200.00;
// نطاق المزامحة يغطي العمود B، وهذه الصيغة تعيش فيه
Sheet.Cells[7, 2].Formula := 'VLOOKUP(A1,B:B,1)';
case Book.Recalculate of
lxOk:
// قبل v2.361.98 كان هذا الفرع غير قابل للوصول لهذه الورقة
SaveReport(Book);
lxErrorRef:
LogWarning('Genuine circular reference - review model inputs');
end;
finally
Book.Free;
end;
end;
ماذا تتخلى عنه باستبعاد حواف المزامحة من الترتيب
شيء واحد بالضبط، ويستحق التصريح به بوضوح لا إخفاءه. لأن حواف المزامحة لا تشارك في الترتيب الطوبولوجي، يمكن تقويم صيغة مزامحة في المرور نفسه قبل أن يعاد حساب بعض الخلايا في نطاق مزامحتها، فتقرأ حينئذ قيمها السابقة. والنتيجة تتقارب في إعادة الحساب التالية
وهذا مقبول لأنه ما يفعله Excel. ففي مصنف بلا حساب تكراري ممكن، جواب Excel نفسه عن قيمة لم يعاد حسابها بعد في المرور الحالي هو القيمة المحسوبة الأخيرة، فالمحرك الذي يستنسخ هذا السلوك يطابق المعالجة المرجعية لا يقرّبها. وإن كنت تحتاج جوابا متقاربا حقا على نموذج يشير إلى ذاته، فالآلية لذلك الحساب التكراري بحد تكرار صريح، وهو مشمول في مقال الحساب التكراري، وينطبق على الدورات الحقيقية لا على تداخلات المزامحة
خطر الانحدار المتواري داخل الإصلاح
أضافت إضافة LookupScan إلى TXLSDepRange خطرا لا علاقة له بالمزامحة وكل علاقته بـ Pascal. فـ TXLSDepRange سجل غير مُدار، فالمتغير المحلي من ذلك النوع لا يهيأ صفريا. وكل موضع في قاعدة الكود يبني واحدا يدويا، بما في ذلك كتل اعتماديات جدول البيانات وعدة مساعدي اختبار، وجب تحديثه إذن لضبط الحقل الجديد صراحة. وفوّت واحدا فيقرر أي بايت صادف وجوده في المكدس هل يعامل ذلك المرجع كحافة مزامحة، وهو ما ينتج عيب إعادة حساب يظهر ويختفي مع تغييرات كود غير ذات صلة
// حقل منطقي جديد في سجل غير مُدار يجعل كل موضع بناء يدوي
// عيبا كامنا. أسلوبان آمنان:
var
R: TXLSDepRange;
begin
FillChar(R, SizeOf(R), 0); // صفّر كل شيء ثم املأ
R.Sheet1 := SheetIndex;
R.Sheet2 := SheetIndex;
R.Row1 := Row; R.Col1 := Col;
R.Row2 := Row; R.Col2 := Col;
// أو اضبط كل حقل، بما فيه الجديد، في كل موضع
R.LookupScan := False;
end;
القاعدة العامة التي أُكسبها: إضافة حقل إلى سجل يبنى على المكدس في أكثر من حفنة مواضع تغيير أعلى خطورة مما تبدو، ولن يساعدك المترجم في إيجاد المواضع. وإن كان السجل قابلا للوصول من مسار ساخن ففضّل مساعدا يهيئه تماما على الوثوق بأن كل موقع استدعاء سيفضل إلى التحديث
التفريق بين دورة حقيقية وتداخل مزامحة
لا شيء في هذا التغيير يضعف كشف الدورات. فـ =B7+1 في B7 ما تزال دورة، وسلسلة من ثلاث صيغ تغلق على ذاتها ما تزال دورة، وكلاهما ما يزال يفيد عبر نتيجة إعادة الحساب مع احتفاظ أعضاء الدورة بقيمها المخزنة السابقة بينما كل ما خارج الدورة يظل حاليا. والتغيير الوحيد أن وسيط مصفوفة المزامحة لم يعد يصنع دورات لا يراها Excel
إن كنت تدقق مصنفا وتريد معرفة أي مراجع حلها المحرك فعلا وبأي ترتيب، فمتتبع التقويم هو الأداة لذلك؛ ومقال متتبع تقويم الصيغ يغطي كيفية قراءة خرجه. HotXLS مكون جداول بيانات أصلي لـ Delphi وC++Builder يقرأ ويكتب XLS وXLSX وODS وCSV دون Excel مثبتا، ومحرك إعادة الحساب هو نفسه على كل صيغة؛ وتغطية الدوال والمحرك الحالية مدرجة في صفحة منتج HotXLS Delphi spreadsheet component