مقال تقني

وظائف الهندسة في Delphi: تحويل القاعدة والرياضيات المعقدة

تُقرأ عائلة الهندسة في Excel كأسهل زاوية في مرجع الوظائف. يحول DEC2BIN رقمًا إلى سلسلة ثنائية. ويحوله HEX2DEC مرة أخرى. يضيف IMSUM رقمين مركبين. يبدو كل واحد منها بمثابة تمرين تنسيق. لكنهم ليسوا كذلك. خلف هذه الأسماء يقبع ترميز المكمل لاثنين ذو العشر بتات الذي لم يمسه معظم المطورين منذ فصول هندسة الكمبيوتر، وتنسيق الأرقام المركبة الذي يعيش بالكامل داخل السلاسل، وعوامل البت التي ستفيض بصمت على عدد صحيح 64 بت (64-bit integer) إذا قمت بالإزاحة قبل التحقق. محرك جدول البيانات الذي يعيد إنتاج Excel تمامًا لا يمكنه تقريب أي من هذا.

تنقسم الوظائف إلى ثلاث مجموعات، وكل مجموعة تخفي فخًا مختلفًا. تحويل القاعدة (Base conversion) يتعلق بالأرقام السالبة والحدود لكل قاعدة. الحساب المعقد (Complex arithmetic) يتعلق بتحليل (parsing) وتنسيق سلسلة (string). معاملات البت (Bitwise operations) تتعلق بالبقاء داخل حدود Int64. يمشي هذا المقال في كل مجموعة كما تنفذها HotXLS، مع استدعاءات ورقة العمل التي ستكتبها فعليًا.

تحويل القاعدة والمكمل لاثنين ذو العشر بتات

الاتجاه الأمامي هو الجزء الذي يتوقعه الجميع. يعطي DEC2BIN(9) "1001"، وتقوم وسيطة (argument) ثانية اختيارية بملء النتيجة من اليسار (left-pads) إلى عرض ثابت. الفخ هو المدخلات السلبية. Excel لا يكتب علامة سالب. بل يشفر القيمة كسلسلة مكملة لاثنين من عشرة أرقام في القاعدة الهدف، وهذا هو السبب في أن DEC2BIN(-5,10) يُرجع "1111111011" بدلاً من أي شيء يحمل علامة. يتم تجاهل وسيطة المنازل (places argument) بمجرد أن تكون القيمة سالبة، لأن الترميز يكون مثبتًا بالفعل عند عشرة أرقام.

عشرة أرقام هي ميزانية ثابتة، وتلك الميزانية تحدد النطاق القابل للتمثيل لكل قاعدة. في النظام الثنائي، الحجم الذي ينقلب إلى النصف السالب هو 512، ومعامل الالتفاف (wrap modulus) هو 1024، وبالتالي فإن السلسلة الثنائية تكون موقعة (signed) فقط عندما يكون طولها بالضبط عشرة أحرف وتكون قيمتها على الأقل 512. تتوسع نفس الفكرة مع القاعدة. يستخدم النظام الثماني (Octal) عتبة نصفية تبلغ 2^29 ومعاملاً كاملاً يبلغ 2^30. يستخدم النظام السداسي عشري (Hexadecimal) 2^39 و 2^40. يطبق قارئ HotXLS هذه القاعدة بالضبط: فهو يراكم الأرقام، وفقط عندما يكون عرض السلسلة عشرة أحرف والقيمة المتراكمة تقع عند أو أعلى من عتبة النصف، فإنه يطرح المعامل الكامل لاستعادة القيمة الموقعة (signed value). السلسلة المكونة من تسعة أحرف تكون دائمًا غير سالبة، بغض النظر عن حجمها.

المشفر (encoder) هو صورة المرآة. يتم تحويل القيمة غير السالبة رقمًا برقم ويتم حشوها بالأصفار اختياريًا إلى العرض المطلوب، ويتم رفضها إذا تجاوزت السقف الموجب للقاعدة أو إذا كان العرض المطلوب ضيقًا جدًا بحيث لا يمكنه استيعابها. يتم أولاً إدخال القيمة السالبة في النطاق عن طريق إضافة المعامل الكامل، مما يحولها إلى قيمة يكون تمثيل قاعدتها دائمًا من عشرة أرقام، ثم يتم إصدار الأرقام بأصفار بادئة لملء العرض. التحقق من النطاق المشترك الوحيد، والحدود الدنيا والعليا المتماثلة لكل قاعدة، هو ما يبقي DEC2BIN و DEC2OCT و DEC2HEX متسقة مع بعضها البعض عند حوافها.

يترك ذلك التحويلات عبر القاعدة، تلك مثل HEX2BIN و OCT2HEX التي تغير القاعدة دون المرور بالنظام العشري في اسم الوظيفة. لا يحمل التنفيذ روتينًا منفصلاً لكل زوج مرتب. يقوم بتحليل السلسلة المدخلة إلى قيمة عشرية موقعة باستخدام القاعدة المصدر، ثم ينسق تلك القيمة العشرية في القاعدة الوجهة. النظام العشري هو المحور (pivot). روتين تحليل واحد وروتين تنسيق واحد، متألفان، يغطيان كل مجموعة، ولأن كلا النصفين يشتركان في نفس اصطلاح الأرقام العشرة الموقعة، فإن القيمة السالبة تنجو من الرحلة مع بقاء علامتها سليمة.

الأرقام المركبة عبارة عن نصوص (strings)، لذا فإن العمل يكمن في التحليل (parsing)

ليس لدى Excel نوع بيانات معقد (complex data type). القيمة المعقدة هي السلسلة "a+bi"، وكل وظيفة في عائلة IM تأخذ هذه السلاسل وتعيد واحدة. يبني COMPLEX السلسلة من جزء حقيقي وجزء خيالي. تقوم وظائف IMSUM و IMSUB و IMPRODUCT و IMDIV بتحليل وسيطاتها، وإجراء العمليات الحسابية على الأجزاء الرقمية، وتنسيق النتيجة مرة أخرى في سلسلة. العمل الرقمي هو الجبر الجامعي. الصعوبة تكمن بالكامل في تحويل النص إلى رقمين بفاصلة عائمة (floating-point numbers) بشكل موثوق، وهنا يكسب المحلل الداخلي (internal parser) قيمته.

تفصيلان في هذا المحلل من السهل الخطأ فيهما. الأول هو الوحدة الخيالية المجردة (bare imaginary unit). السلسلة "i" تعني واحد مضروبًا في i، وليس صفرًا ولا خطأ، لذلك عندما يكون المعامل (coefficient) أمام اللاحقة (suffix) فارغًا أو علامة زائد وحيدة، يجب على المحلل قراءتها كقيمة 1، وعلامة ناقص وحيدة كـ -1. تخطى ذلك ويتوقف IMSUM("i","i") عن كونه 2i. الثاني هو التدوين العلمي الذي يصطدم بالعلامة التي تفصل الأجزاء الحقيقية والخيالية. يجد المحلل هذا الفاصل من خلال البحث عن علامة زائد أو ناقص، لكن الرقم المكتوب كـ "1.5E-3" يحتوي على علامة ناقص تنتمي إلى الأس. وبالتالي يرفض الفحص التعامل مع علامة الزائد أو الناقص كفاصل عندما يكون الحرف الذي يسبقها مباشرة هو e أو E. بدون هذا الحارس، سيتم تمزيق الجزء الحقيقي إلى النصف عند علامة الأس وسيفشل التحليل في مدخلات صالحة تمامًا.

يتم الحفاظ على اللاحقة نفسها بدلاً من تطبيعها. يقبل Excel كلاً من i و j، وتتذكر HotXLS أيهما استخدمه الإدخال بحيث تحمل النتيجة المنسقة نفس الحرف. يطبق التنسيق بعد ذلك الاختصارات التقليدية: يُطبع الجزء الخيالي المكون من واحد كلاحقة فقط، وسالب واحد كـ -i، والجزء الخيالي الصفري ينهار إلى حقيقي عادي، والجزء الحقيقي الصفري يُسقط البادئة 0+.

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Engineering');
    // Negative input: a ten-bit two's complement, places argument ignored.
    Sheet.Cells[1, 1].Value := Sheet.Calculate('=DEC2BIN(-5,10)'); // 1111111011
    // Complex multiply on two "a+bi" strings.
    Sheet.Cells[2, 1].Value := Sheet.Calculate('=IMPRODUCT("3+4i","1+2i")'); // -5+10i
  finally
    Book.Free;
  end;
end;

الوظائف المعقدة المتسامية (transcendental complex functions)، ومن بينها IMSQRT و IMEXP و IMLN و IMPOWER، لا تعمل في الإحداثيات المستطيلة. يقومون بتحويل القيمة التي تم تحليلها إلى شكل قطبي (polar form)، وتطبيق العملية على المعيار (modulus) والوسيطة (argument)، وتحويلها مرة أخرى. يقسم الجذر التربيعي الوسيطة إلى نصفين ويأخذ جذر المعيار. تضرب القوة الوسيطة وترفع المعيار. القيام بذلك بأي طريقة أخرى سيعني إعادة اشتقاق كل هوية في شكل مستطيل، وهو ما يعني كودًا أكثر وأقل استقرارًا عدديًا بالقرب من القطع الفرعي (branch cuts).

مُعامِلات البت (Bitwise operators) وتجاوز السعة (overflow) الذي يجب التحقق منه أولاً

أضاف Excel 2013 BITAND و BITOR و BITXOR و BITLSHIFT و BITRSHIFT. المعاملات (operands) مقيدة: يجب أن يكون كل منها عددًا صحيحًا غير سالب لا يزيد عن 2^48 ناقص 1، وأي وسيطة كسرية أو سالبة تمثل خطأ رقميًا. هذا الحد الأقصى سخي بما يكفي لتغطية أي مجموعة علامات (flag set) واقعية مع البقاء جيدًا داخل النطاق القابل للتمثيل بدقة للرقم المزدوج (double)، وهو أمر مهم لأن Excel يمرر كل وسيطة رقمية كقيمة فاصلة عائمة (floating-point value).

تحمل وظائف الإزاحة قاعدة ترتيب واحدة تلدغ حقًا. يمكن أن تنتج الإزاحة إلى اليسار قيمة أكبر بكثير من مدخلاتها، وإذا قمت بتنفيذ shl أولاً وفحصت النتيجة بعد ذلك، فستكون قد تجاوزت سعة Int64 بالفعل وسيكون الاختبار بلا معنى. يجب أن يأتي التحقق قبل الإزاحة. تقارن HotXLS المعامل (operand) مقابل السقف المزاح إلى اليمين بمقدار مقدار الإزاحة، وفقط إذا كان المعامل مناسبًا فإنها تقوم بالإزاحة اليسرى الفعلية. يُرفض مقدار الإزاحة الذي يتجاوز 53 بت بشكل صريح، وتعكس الإزاحة السالبة الاتجاه ببساطة، لذا فإن BITLSHIFT مع عدد سالب يتصرف كإزاحة إلى اليمين. يعمم المبدأ إلى ما هو أبعد من هذه الوظيفة الواحدة: عندما يوجد حارس (guard) لمنع تجاوز السعة (overflow)، يجب أن يتم تشغيله على المدخلات، وليس أبدًا على النتيجة التي كان من المفترض أن يحميها.

// Bitwise calls evaluate the same way through Calculate.
Sheet.Cells[3, 1].Value := Sheet.Calculate('=BITAND(13,11)');    // 9
Sheet.Cells[4, 1].Value := Sheet.Calculate('=BITLSHIFT(5,2)');   // 20
Sheet.Cells[5, 1].Value := Sheet.Calculate('=BITRSHIFT(40,3)');  // 5

الوظائف المستقبلية وبادئة الاسم _xlfn

تتفاعل عوامل البت (bitwise operators) وقائمة طويلة من الإضافات الأخرى بعد عام 2007 مع نظام تسمية لا علاقة له بما تحسبه وكل ما له علاقة بكيفية تخزين Excel لها. خصص تنسيق ورقة العمل الثنائية الأصلية لكل وظيفة مدمجة (built-in function) فتحة رقمية في جدول ثابت. الوظائف التي تم اختراعها بعد تجميد هذا الجدول لا تحتوي على فتحة. لحفظ مثل هذه الوظيفة في ملف وجعل برنامج Excel حديثًا يتعرف عليها، تتم كتابة الاسم ببادئة _xlfn.، لذلك يتم تخزين BITAND كـ _xlfn.BITAND على القرص على الرغم من أن المستخدم لا يكتب سوى BITAND دائمًا.

المشكلة هي أن القاعدة ليست موحدة. تم منح بعض الوظائف الأحدث فتحات جدول وتُكتب مجردة، في حين تُكتب بعض الوظائف المخفية القديمة أيضًا بدون بادئة على الرغم من عمرها. تحتفظ HotXLS بقائمة بيضاء (whitelist) صريحة للأسماء التي تحتاج إلى البادئة، وتضيفها عند الكتابة وتزيلها عند القراءة، لذا فإن نص الصيغة (formula text) الذي قمت بتعيينه وتقرأه مرة أخرى يكون دائمًا الاسم النظيف المواجه لـ Excel. تقوم بتعيين =BITLSHIFT(5,2)، ويحمل الملف _xlfn.BITLSHIFT، وتعود القيمة على أنها 20 بغض النظر. البادئة هي تفصيل تخزين لا ينبغي أبدًا أن يتسرب إلى الصيغ التي تعمل بها في التعليمات البرمجية (code).

تجميع كل ذلك في ورقة عمل

السطح العام لكل هذا صغير. قم بإنشاء TXLSXWorkbook، وأضف ورقة عمل، وإما اكتب صيغة في خلية عبر Cells[Row, Col].Formula وأعد الحساب، أو قم بتقييم تعبير مباشرة باستخدام طريقة Calculate لورقة العمل، والتي تقوم بترجمة الصيغة مقابل تلك الورقة وتُرجع Variant. تستخدم الأمثلة أعلاه Calculate لأنها تظهر نتيجة استدعاء هندسي واحد دون حالة الورقة المحيطة به، لكن نفس الوظائف تُقيم بشكل متطابق داخل صيغ الخلية الحقيقية عند إعادة حساب المصنف.

الترميزات هي الجزء الذي يجب وضعه في الاعتبار، وليس مواقع الاستدعاء (call sites). السلسلة الثنائية تكون موقعة فقط عند عشرة أرقام وفقط بعد عتبة النصف لقاعدتها. الرقم المركب هو نص، ومعامل خيالي فارغ هو واحد، ويقفز المحلل فوق الحرف e الخاص بالأس. يتم التحقق من الإزاحة اليسرى قبل إزاحتها. احصل على هذه الحقائق الأربع بشكل صحيح وستتوقف عائلة الهندسة عن كونها مصدرًا للمفاجآت بسبب العلامات (off-by-a-sign).

إذا كنت تقوم بتوصيل رياضيات النطاق (domain math) الخاصة بك في نفس المحرك، فإن آليات تسجيل معالج (handler) وإرجاع القيم تمت تغطيتها في مقالنا حول توسيع محرك الصيغة بوظائف مخصصة، وعندما تضطر هذه الصيغ إلى الوصول عبر الأوراق بالاسم بدلاً من عنوان الخلية، فإن الشرح التفصيلي حول الأسماء المحددة (defined names) والصيغ عبر الأوراق يوضح كيف يتم حل المراجع. تُشحن الوظائف الهندسية الموضحة هنا كجزء من مكون جدول بيانات HotXLS لـ Delphi و C++Builder، جنبًا إلى جنب مع واجهات برمجة تطبيقات القراءة والكتابة والحساب المغطاة في مكان آخر على هذه المدونة.