مقاله فنی

فرمول آرایه‌ای HotXLS: چرا Excel علامت @ و #VALUE! می‌گذارد

‏Excel 365 وقتی فایل یک فرمول مثل =SUM(A1:B1*{10,100}) را به‌صورت فرمول معمولی ذخیره کرده باشد، داخلش @ درج می‌کند و #VALUE! نشان می‌دهد، چون Excel در آن حالت تقاطع ضمنی legacy را روی تک‌تک عملوندهای عملگر اعمال می‌کند. از v2.384.68 به بعد، HotXLS Delphi Component این فرمول‌های آرایه‌ای با عملگر را همان‌طور که Excel 365 انجام می‌دهد ذخیره می‌کند: در XLSX به‌شکل فرمول‌های آرایه پویای تک‌سلولی و در XLS به‌شکل فرمول‌های آرایه‌ای تک‌سلولی

این symptom از code review جان سالم به در می‌برد. سرویس Delphi شما یک workbook می‌نویسد، HotXLS آن را دوباره محاسبه می‌کند و برای =SUM(A1:B1*{10,100}) مقدار 210 را کش می‌کند، و مشتری فایل را در Excel 16 باز می‌کند و در نوار فرمول =SUM(@A1:B1*@{10,100}) و در سلول #VALUE! می‌بیند. هیچ‌جای فایل کج نیست. چیزی که غایب است metadataای است که به Excel می‌گوید این فرمول زیر قواعد آرایه پویا نوشته شده، و بدون آن Excel به مدل ارزیابی قبل از آرایه پویا برمی‌گردد

چرا Excel 365 به فرمولی که HotXLS درست محاسبه کرده علامت @ اضافه می‌کند؟

Excel 365 علامت @ را اضافه می‌کند چون فرمولی که علامت‌گذاری آرایه پویا ندارد طبق تعریف یک فرمول legacy است، و فرمول‌های legacy هرجا که یک عملگر انتظار تک‌مقدار دارد یک محدوده چندسلولی را به یک سلول می‌کاهند. این کاهش همان تقاطع ضمنی است: Excel سلولی از محدوده را برمی‌دارد که با سطر فرمول (برای محدوده عمودی) یا ستون فرمول (برای محدوده افقی) مشترک است، و اگر چنین سلولی وجود نداشته باشد نتیجه #VALUE! می‌شود. Excel 365 همین معنا را برای فرمول‌های old-style نگه می‌دارد و @ را نمایش می‌دهد تا این کاهش دیده شود

‏=SUM(A1:B1*{10,100}) را در E5 بگذارید تا خوانش legacy آشکار شود. ‏A1:B1 یک محدوده افقی است، فرمول در ستون E نشسته، محدوده هیچ سلولی در ستون E ندارد، پس @A1:B1 برابر #VALUE! است و کل SUM همان را به ارث می‌برد. زیر قواعد آرایه پویا همان متن ضرب را المان به المان انجام می‌دهد، 1 × 10 + 2 × 100، و 210 برمی‌گرداند. موتور فرمول HotXLS از نسخه‌های v2.384.61 و v2.384.63 به شیوه آرایه پویا ارزیابی می‌کرد؛ فقط فرمت فایل این را اعلام نمی‌کرد. با A1:B2 که 1 و 2 و 3 و 4 را نگه می‌دارد، این فرمول‌های آزمایشی و خروجی‌شان در Excel 16 هستند:

نمودار HotXLS که تقاطع ضمنی و ارزیابی آرایه پویای SUM(A1:B1*{10,100}) را در سلول E5 مقایسه می‌کند: مدل legacy هیچ سلولی از محدوده افقی A1:B1 در ستون E پیدا نمی‌کند و #VALUE! برمی‌گرداند، درحالی‌که مدل آرایه پویا 1 را در 10 و 2 را در 100 ضرب می‌کند و 210 برمی‌گرداند
Excel داخل فرمول معمولی @ درج می‌کند و #VALUE! نشان می‌دهد، چون تقاطع ضمنی در ستون E چیزی پیدا نمی‌کند؛ با علامت‌گذاری آرایه پویای HotXLS همان فرمول المان به المان ضرب می‌کند و روی 210 فرود می‌آید
فرمولنتیجه HotXLSExcel 16، ذخیره‌شده به‌عنوان فرمول معمولیذخیره از v2.384.68 به بعد
=SUM(A1:B1*{10,100})210#VALUE!آرایه پویا، Excel نشان می‌دهد 210
=SUM((A1:B2>2)*1)2تقاطع ضمنی، نتیجه غلط یا خطاآرایه پویا، Excel نشان می‌دهد 2
=SUMPRODUCT((A1:B2>2)*1)2تقاطع ضمنی، نتیجه غلط یا خطاآرایه پویا، Excel نشان می‌دهد 2
=MAX(A1:B2-1)3تقاطع ضمنی، نتیجه غلط یا خطاآرایه پویا، Excel نشان می‌دهد 3
=SUM(A1:B2)1010فرمول معمولی، بدون تغییر

سطر آخر به‌اندازه چهار سطر اول اهمیت دارد. ‏SUM(A1:B2) یک محدوده را مستقیم به پارامتری از تابع می‌دهد که ارجاع می‌پذیرد، پس هیچ عملگری هرگز محدوده چندسلولی نمی‌بیند و هیچ تقاطعی در کار نیست. خود Excel 365 هم آن فرمول را به‌صورت فرمول معمولی ذخیره می‌کند، و HotXLS هم همین کار را می‌کند

HotXLS فرمول‌های آرایه‌ای با عملگر را چطور در XLSX و XLS ذخیره می‌کند

HotXLS یک فرمول آرایه‌ای با عملگر را در XLSX به‌شکل آرایه پویای تک‌سلولی می‌نویسد: المنت <c> مقدار cm="1" را حمل می‌کند، فرمول <f t="array" ref="E5"> است، و پکیج xl/metadata.xml می‌گیرد با یک نوع metadata از جنس XLDAPR که extension آن dynamicArrayProperties fDynamic="1" را نگه می‌دارد. ویژگی cm یک اندیس یک‌مبنا به بلاک cellMetadata در همان part است، و رکورد XLDAPR پشتش همان چیزی است که به Excel می‌گوید «این را زیر قواعد آرایه پویا ارزیابی کن». این همان ساختاری است که Excel 16 وقتی همان فرمول را تایپ و ذخیره می‌کنید می‌نویسد، و target layout هم از همین راه مشخص شد

در XLS هیچ part متادیتایی وجود ندارد، پس HotXLS از تنها سازه‌ای استفاده می‌کند که BIFF8 برای ارزیابی آرایه دارد: فرمول آرایه‌ای تک‌سلولی. سلول یک رکورد FORMULA می‌گیرد که token stream آن یک PtgExp منفرد است که به خود سلول اشاره می‌کند، و بعد از آن یک رکورد ARRAY یعنی $0221 می‌آید که فرمول parse‌شده واقعی را روی بازه تک‌سلولی حمل می‌کند. Excel 365 فرمول‌های آرایه پویا را به همان شکل در XLS می‌نویسد، و یک نسخه قدیمی‌تر Excel که فایل را باز کند یک فرمول آرایه‌ای کلاسیک با Ctrl+Shift+Enter می‌بیند

نمودار ذخیره‌سازی HotXLS برای فرمول آرایه‌ای با عملگر SUM(A1:B1*{10,100}): موتور XLSX یک آرایه پویای تک‌سلولی با cm برابر 1 می‌نویسد، یک المنت f از نوع array و یک رکورد XLDAPR در xl/metadata.xml که GUID حروف‌کوچکش الزامی است، درحالی‌که موتور XLS رکورد FORMULA با PtgExp به‌علاوه رکورد ARRAY یعنی 0221 می‌نویسد
موتور XLSX سلول را با cm=1 به‌علاوه یک رکورد metadata از نوع XLDAPR علامت می‌زند و موتور کلاسیک یک FORMULA با PtgExp را با یک رکورد ARRAY روی یک سلول جفت می‌کند؛ Excel 365 هم آرایه‌های پویا را به همان شکل در XLS ذخیره می‌کند

هیچ API جدیدی در کار نیست. علامت‌گذاری همان موقع اتفاق می‌افتد که فرمول را از طریق API معمولی سلول assign کنید، در هر دو موتور. در سمت XLSX این TXLSXCell.Formula است:

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Data');
    Sheet.Cells[1, 1].Value := 1;
    Sheet.Cells[1, 2].Value := 2;
    Sheet.Cells[2, 1].Value := 3;
    Sheet.Cells[2, 2].Value := 4;

    // عملگر روی یک محدوده یا آرایه درون‌خطی: به‌صورت آرایه پویا ذخیره می‌شود
    Sheet.Cells[5, 5].Formula := '=SUM(A1:B1*{10,100})';
    Sheet.Cells[6, 5].Formula := '=SUM((A1:B2>2)*1)';
    // محدوده‌ای که مستقیم به یک تابع داده می‌شود: یک <f> معمولی می‌ماند
    Sheet.Cells[7, 5].Formula := '=SUM(A1:B2)';

    if Book.Recalculate = lxOk then
      Writeln(VarToStr(Sheet.Cells[5, 5].Value));   // 210

    // ریشه آرایه متن خودش را بدون علامت = ابتدایی نگه می‌دارد
    Writeln(Sheet.Cells[5, 5].Formula);              // SUM(A1:B1*{10,100})

    Book.SaveAs('probe.xlsx');   // E5 و E6 مقدار cm="1" + t="array" می‌گیرند
  finally
    Book.Free;
  end;
end;

بعد از تبدیل، TXLSXCell.Formula متن را بدون = برمی‌گرداند، همان شکلی که TXLSXRange.SetDynamicArrayFormula ذخیره می‌کند، پس کدی که بعد از assign رشته‌های فرمول را مقایسه می‌کند باید علامت = ابتدایی را normalize کند

موتور کلاسیک همان قاعده را از طریق IXLSRange.Formula روی یک سلول منفرد دنبال می‌کند. assign کردن فرمول آن را در درون به مسیر آرایه تک‌سلولی هدایت می‌کند، پس XLS ذخیره‌شده جفت FORMULA به‌علاوه ARRAY را دارد:

var
  Wb: IXLSWorkbook;
  Sh: TXLSWorksheet;
begin
  Wb := TXLSWorkbook.Create;
  Sh := Wb.Sheets.Add;
  Sh.Range['A1', 'A1'].Value := 1;
  Sh.Range['B1', 'B1'].Value := 2;
  Sh.Range['A2', 'A2'].Value := 3;
  Sh.Range['B2', 'B2'].Value := 4;

  Sh.Range['E5', 'E5'].Formula := '=SUM(A1:B1*{10,100})';  // رکورد ARRAY
  Sh.Range['E6', 'E6'].Formula := '=MAX(A1:B2-1)';         // رکورد ARRAY
  Sh.Range['E7', 'E7'].Formula := '=SUM(A1:B2)';           // FORMULA معمولی

  Writeln(VarToStr(Sh.Range['E5', 'E5'].Value));   // 210
  Writeln(VarToStr(Sh.Range['E6', 'E6'].Value));   // 3
  Wb.SaveAs('probe.xls');
end;

اگر به‌جای یک جمع اسکالر دنبال لنگر کردن یک نتیجه چندسلولی هستید، APIهای صریح همچنان ابزار درست‌اند: SetArrayFormula برای یک مستطیل از پیش اندازه‌گرفته، همان‌طور که در فرمول‌های سرریز آرایه پویا در دلفی با HotXLS توضیح داده شده، یا TXLSXRange.SetDynamicArrayFormula برای وقتی که علامت‌گذاری آرایه پویای XLSX را روی بازه‌ای که خودتان اندازه می‌گیرید می‌خواهید. مسیر خودکار این مقاله فقط فرمول‌هایی را پوشش می‌دهد که در یک سلول تایپ می‌شوند

HotXLS کدام فرمول‌ها را به‌عنوان آرایه پویا علامت می‌زند؟

HotXLS یک فرمول را فقط وقتی علامت می‌زند که یکی از عملگرها زیردرخت عملوندی داشته باشد که آرایه تولید می‌کند. این چک روی syntax tree کامپایل‌شده اجرا می‌شود، و یک عملوند وقتی آرایه تولید می‌کند که محدوده‌ای چندسلولی باشد، یک ثابت آرایه درون‌خطی باشد، یا یک عبارت عملگری دیگر که خودش چنین عملوندی دارد. پرانتزها شفاف‌اند. عملگرهایی که شمرده می‌شوند عبارت‌اند از عملگرهای حسابی یعنی + - * / ^، الحاق یعنی &، شش مقایسه، مثبت و منفی یگانی، و درصد:

  • A1:B1*{10,100}، (A1:B2>2)*1، --(B1:B2>0) و A1:B2-1 علامت می‌خورند، هر جای فرمول که باشند، از جمله داخل SUMPRODUCT
  • SUM(A1:B2) و SUMPRODUCT(A1:A2,{1;10}) علامت نمی‌خورند، چون محدوده و آرایه مستقیم وارد آرگومان تابع می‌شوند و هیچ عملگری به آن‌ها دست نمی‌زند
  • A1*2 یا SUM(A1,B1)*2 علامت نمی‌خورند: ارجاع‌های تک‌سلولی و خروجی تابع‌ها از دید این چک اسکالرند

سه مرز عمدی است. اول، علامت‌گذاری فقط وقتی رخ می‌دهد که فرمول از طریق API وارد شود؛ یعنی TXLSXCell.Formula در موتور XLSX و assign کردن Formula یا Value تک‌سلولی در موتور کلاسیک. فرمول‌هایی که از فایل load می‌شوند دقیقاً همان‌طور که پیدا شده‌اند نوشته می‌شوند، چون یک فرمول legacy از تولیدکننده‌ای دیگر ممکن است عمداً به تقاطع ضمنی وابسته باشد. دوم، متنی که نه : دارد نه { بدون کامپایل دوباره رد می‌شود. سوم، فرمولی که سرریز می‌کرد، مثل =A1:B1*2 به‌تنهایی، به‌عنوان آرایه پویای تک‌سلولی علامت می‌خورد که همان‌جا که گذاشته‌ایدش لنگر است. HotXLS آن را سرریز نمی‌کند، و Excel دفعه بعد که دوباره محاسبه کند نتیجه را به سلول‌های مجاور گسترش می‌دهد

این قاعده عملوند برادر قاعده کلاس-آرگومان است که در تقاطع ضمنی نام‌های تعریف‌شده در HotXLS برای Delphi پوشش داده شده. آن مقاله درباره پارامترهای تابع است که با کلاس value اعلان شده‌اند؛ این یکی درباره عملگرهاست که در مدل legacy همیشه مقدار می‌خواهند

در موتور محاسبه چه چیزی عوض شد تا نتایج یکی شوند

فیکس ذخیره‌سازی در v2.384.68 بر این تکیه دارد که موتور فرمول HotXLS از قبل مقادیر Excel 365 را برمی‌گرداند، که خودش چند فیکس قبلی در هر دو موتور لازم داشت. پیداترین‌شان SUMPRODUCT بود: تا v2.384.61 فقط دو محدوده ساده یا بیشتر می‌پذیرفت، پس SUMPRODUCT((B1:B2>0)*1) و SUMPRODUCT(--(B1:B2>0)) و حتی SUMPRODUCT(B1:B2) تک‌آرگومان هم #N/A برمی‌گرداندند. HotXLS حالا آرگومان‌های عبارتی را با قواعد Excel المان به المان ارزیابی می‌کند:

  • هر آرگومان باید دقیقاً شکل یکسانی داشته باشد، یک اسکالر 1 × 1 حساب می‌شود، وگرنه نتیجه #VALUE! است
  • یک مقدار خطا داخل هر آرگومان، خودش به‌عنوان نتیجه برگردانده می‌شود
  • المان‌های متنی و منطقی صفر حساب می‌شوند، پس برای تبدیل TRUE به 1 هنوز (B1:B2>0)*1 یا -- لازم است
  • آرگومان‌هایی که همه محدوده‌های ساده‌اند همان حلقه استریمی اصلی را نگه می‌دارند، پس محدوده‌های بزرگ به‌شکل آرایه materialize نمی‌شوند

خانواده SUM یعنی SUM و COUNT و AVERAGE و MIN و MAX و COUNTA وقتی آرگومانش یک عبارت عملگری روی محدوده باشد از همان ارزیاب المان به المان استفاده می‌کند، پس =SUM((B1:B2>0)*1) هر دو سطر را می‌شمارد نه فقط سلول اول را. ‏v2.384.62 کاری کرد عملگر تقاطع با فاصله مستطیل مشترک دو ارجاع را برگرداند، با #NULL! وقتی هم‌پوشان نیستند، پس =SUM(A1:B2 B1:B2) برابر 6 است نه 2 و نتیجه می‌تواند به پارامترهای ارجاعی مثل ROWS و INDEX بخورد. ‏v2.384.63 ثابت‌های آرایه درون‌خطی مثل {1,2;3,4} (کاما ستون جدا می‌کند، سمی‌کالن سطر) و اجتماع ارجاع‌ها مثل (A1:B2,D4) را به parser اضافه کرد. مقایسه‌های المان به المان هم به یک المان خالی نوع سمت دیگر را می‌دهند، FALSE مقابل یک منطقی، هماهنگ با قاعده اسکالر از v2.384.53 که در زنجیره‌های مقایسه، سلول‌های خالی و SUMIF در HotXLS دلفی توضیح داده شده

var
  V: Variant;
begin
  // Book همان TXLSXWorkbook از مثال اول است؛
  // شیت فعالش A1:B2 برابر 1 و 2 و 3 و 4 را نگه می‌دارد
  V := Book.Calculate('=SUMPRODUCT((A1:B2>2)*1)');   // 2
  V := Book.Calculate('=SUMPRODUCT(A1:B2)');          // 10، آرگومان تکی
  V := Book.Calculate('=SUMPRODUCT(A1:A2,{1;10})');   // 31 = 1*1 + 3*10
  V := Book.Calculate('=SUM(A1:B2 B1:B2)');           // 6، محدوده مشترک B1:B2
  V := Book.Calculate('=SUM((A1:B2,B1:B2))');         // 16، هم‌پوشانی دوبار شمرده می‌شود
  V := Book.Calculate('=ROWS({1,2,3;4,5,6})');        // 2
  V := Book.Calculate('=TRUE*1');                     // 1، قبل از v2.384.61 برابر -1 بود
end;

‏TXLSXWorkbook.Calculate یک رشته فرمول را روی شیت فعال بدون ذخیره کردن ارزیابی می‌کند، راهی سریع برای چک کردن رفتار موتور. یک هشدار درباره خود @: HotXLS از گذشته @ بین دو ارجاع را به‌عنوان تقاطع باینری می‌پذیرفت، و حالا آن شکل را با معناشناسی تقاطع واقعی ارزیابی می‌کند. در Excel 365 علامت @ یک پیشوند یگانی تقاطع ضمنی است. در متن فرمول @ ننویسید و انتظار معنای Excel را داشته باشید؛ برای تقاطع از فاصله استفاده کنید و بگذارید قواعد ذخیره‌سازی بالا معناشناسی آرایه پویا را مدیریت کنند

چرا Excel از باز کردن فایل سر باز می‌زد یا مقدار غلط محاسبه می‌کرد؟

قانع کردن Excel برای پذیرش علامت‌گذاری آرایه پویا سه فیکس لازم داشت که هیچ تست round-trip با خودشان نمی‌توانست بگیردشان، چون HotXLS در همه حالات خروجی خودش را درست می‌خواند. هر کدام با باز کردن خروجی HotXLS در Excel 16 و عوض کردن یک متغیر در هر بار پیدا شد:

  1. GUID ثابت extension باید کاملاً با حروف کوچک باشد. مقدار ext uri در xl/metadata.xml باید دقیقاً {bdbb8cdc-fa1e-496e-a857-3c3f30c029c3} باشد. یک template قدیمی HotXLS آن را با حروف بزرگ‌وکوچک مخلوط نوشته بود، و Excel 16 از باز کردن کل پکیج سر باز می‌کرد، نه فقط آن سلول. workbookهایی که قبل از v2.384.68 با TXLSXRange.SetDynamicArrayFormula ساخته شده بودند همین مشکل را داشتند
  2. متن ریشه آرایه علامت = ابتدایی ندارد. نویسنده XLSX متن ذخیره‌شده ریشه آرایه را عیناً در <f> می‌ریزد. اگر سلول تبدیل‌شده = خودش را نگه می‌داشت، المنت می‌شد <f t="array" ref="E5">=SUM(...)</f> که Excel در زمان open آن را هم رد می‌کند. HotXLS همان موقع تبدیل آن را حذف می‌کند، و برای همین TXLSXCell.Formula بدون آن برمی‌گردد
  3. Double(True) در Delphi برابر -1 است. تبدیل Variant قرارداد COM را دنبال می‌کند که در آن TRUE یعنی همه بیت‌ها یک، و VarIsNumeric(True) هم True برمی‌گرداند. قبل از v2.384.61 این باعث می‌شد =TRUE*1 برابر -1 شود و المان‌های منطقی آرایه به‌عنوان عدد طبقه‌بندی شوند، پس مقایسه‌ای مثل (B1:B2>0)=TRUE غلط از آب درمی‌آمد. HotXLS حالا قبل از اینکه یک Variant را در حساب اسکالر و حساب آرایه و طبقه‌بندی المان آرایه عدد بگیرد varBoolean را چک می‌کند، و TRUE برابر 1 حساب می‌شود

کلاس‌های عملوند BIFF8: جزئیات در سطح byte برای پیاده‌کننده‌های فرمت

در BIFF8 هر token عملوند کلاس عملوندش را در خود بایت token حمل می‌کند، و Excel به آن کلاس بیشتر از ساختار فرمول اعتماد می‌کند. ‏[MS-XLS] کلاس را فیلد دوبیتی PtgDataType در بیت‌های 5 و 6 token تعریف می‌کند: 1 برای ارجاع، 2 برای مقدار، 3 برای آرایه. پنج بیت پایین نام token را می‌گویند، پس همان ارجاع area سه املای متفاوت دارد:

Tokenکلاس ارجاعکلاس مقدارکلاس آرایه
PtgRef$24$44$64
PtgArea$25$45$65
PtgArray$20$40$60

HotXLS سه‌تای این‌ها را در جای جای کد غلط نوشته بود، و هر کدام در Excel symptom متمایزی تولید می‌کرد در حالی که در HotXLS بدون خطا خوانده می‌شد:

  • ثابت‌های آرایه با کلاس ارجاع. انکودر کلاس را از context انتخاب می‌کرد، و پارامترهای SUM یا ROWS کلاس ارجاع دارند، پس =SUM({1,2}) با PtgArray برابر $20 نوشته می‌شد. Excel کل فرمول را به‌شکل =#N/A نشان می‌داد. یک ثابت آرایه هرگز نمی‌تواند ارجاع باشد، پس از v2.384.63 HotXLS هرجا context کلاس ارجاع می‌خواهد کلاس آرایه یعنی $60 می‌نویسد
  • عملوندهای PtgIsect و PtgUnion با کلاس مقدار. عملگرهای باینری عملوند کلاس مقدار می‌گرفتند، که برای * درست است اما برای عملگرهای ارجاعی غلط. با areaهای $45 قبل از PtgIsect یعنی $0F، Excel ‏=SUM(A1:B2 B1:B2) را مثل =SUM(@A1:B2 @B1:B2) می‌خواند و #VALUE! برمی‌گرداند. از v2.384.62 عملوندهای PtgIsect و PtgUnion یعنی $10 با کلاس ارجاع یعنی $25 نوشته می‌شوند
  • عملوندهای کلاس مقدار داخل رکورد ARRAY. Excel حتی داخل یک فرمول آرایه‌ای وقتی عملوند کلاس مقدار باشد تقاطع ضمنی اعمال می‌کند. HotXLS آنجا $45 می‌نوشت، پس فرمول آرایه تک‌سلولی =SUM(A1:B1*{10,100}) در Excel به 10 ارزیابی می‌شد. از v2.384.68 به بعد token stream یک رکورد ARRAY هر ارجاع کلاس-مقدار و ثابت آرایه را به کلاس آرایه یعنی $65 و $60 ارتقا می‌دهد، که همان چیزی است که Excel می‌نویسد
نمودار BIFF8 در HotXLS: بیت‌های 5 و 6 هر بایت token کلاس ارجاع یا مقدار یا آرایه را انتخاب می‌کنند، پس PtgArea با املاهای 25 و 45 و 65 نوشته می‌شود، همراه سه نقص فیکس‌شده: ثابت‌های آرایه با 20 یعنی #N/A نشان داده می‌شد، عملوندهای PtgIsect با 45 یعنی #VALUE! برمی‌گشت، و عملوندهای رکورد ARRAY با 45 باعث می‌شد SUM(A1:B1*{10,100}) برابر 10 شود
هر token عملوند BIFF8 کلاسش را در بیت‌های 5 و 6 حمل می‌کند و Excel به آن بیت‌ها بیشتر از ساختار اعتماد می‌کند؛ HotXLS ثابت‌های آرایه را 60 می‌نویسد، عملوندهای PtgIsect را 25، و tokenهای رکورد ARRAY را به کلاس آرایه ارتقا می‌دهد

یک reader که بیت‌های کلاس را نادیده می‌گیرد هر سه را بی‌دردسر round-trip می‌کند، پس اگر BIFF8 writer خودتان را نگهداری می‌کنید، بیت‌های کلاس هر token عملوند را با یک فایل ذخیره‌شده توسط Excel از همان فرمول مقایسه کنید، نه فقط با شماره tokenها

مرجع سریع

  • Excel 365 وقتی یک عملگر در فرمول معمولی و بدون علامت یک محدوده چندسلولی یا آرایه درون‌خطی بگیرد @ نشان می‌دهد
  • HotXLS از v2.384.68 به بعد چنین فرمول‌هایی را به‌شکل آرایه پویای تک‌سلولی در XLSX یعنی cm="1" و t="array" و metadata از نوع XLDAPR، و به‌شکل فرمول آرایه تک‌سلولی در XLS یعنی FORMULA با PtgExp به‌علاوه ARRAY یعنی $0221 ذخیره می‌کند
  • فقط عملوندهای عملگر شمرده می‌شوند؛ محدوده‌ای که مستقیم به آرگومان تابع می‌رود فرمول معمولی می‌ماند
  • فقط فرمول‌هایی که از طریق TXLSXCell.Formula یا Formula / Value کلاسیک تک‌سلولی وارد شده‌اند علامت می‌خورند؛ فرمول‌های loadشده دست‌نخورده می‌مانند
  • سلول ریشه تبدیل‌شده بدون علامت = ابتدایی برمی‌گردد
  • GUID یعنی ext uri مربوط به آرایه پویا باید حروف کوچک باشد وگرنه Excel پکیج را رد می‌کند
  • در Delphi مقدار Double(True) برابر -1 است؛ قبل از تبدیل عددی varBoolean را تست کنید
  • BIFF8: ثابت‌های آرایه هرگز کلاس ارجاع، عملوندهای PtgIsect / PtgUnion با کلاس ارجاع، و عملوندهای رکورد ARRAY با کلاس آرایه

HotXLS workbookهای XLS و XLSX را به‌صورت بومی از Delphi و C++Builder می‌خواند و می‌نویسد و محاسبه می‌کند و فرمول‌های آرایه‌ای با عملگر را طوری ذخیره می‌کند که Excel 365 آن‌ها را با همان مقادیری که HotXLS محاسبه کرده باز کند. برای نسخه‌ها، مستندات و دانلود آزمایشی کامپوننت صفحه‌گسترده HotXLS در Delphi را ببینید