Műszaki cikk

HotXLS keresővizsgálatok és téves körkörös hivatkozások

Tegyen egy =VLOOKUP(A1,B:B,1) képletet a B oszlop egy cellájába, és az Excel panasz nélkül kiszámolja. Adja ugyanazt a munkafüzetet egy függőséggráf alapú újraszámolási motornak, és körkörös hivatkozási hibát kaphat, mert a képlet egy olyan tartománytól függ, amely tartalmazza a képletet. A HotXLS pontosan ezt jelentette a v2.361.98-ig. A javítás nem külön eset a teljes oszlopos tartományokra; két függőségi éltípus megkülönböztetése, amelyre egy táblázatmotornak szüksége van, és amelyet egy sima irányított gráf nem tud

A keresőcsalád — LOOKUP, MATCH, HLOOKUP, VLOOKUP, XLOOKUP és XMATCH — keresőtömb argumentuma mostantól vizsgálati hivatkozásként van jelölve. Egy vizsgálati hivatkozás továbbra is beoltja a szennyezettséget, tehát a tartományon belüli cella szerkesztése újraszámolja a képletet, de soha nem járul hozzá a körészleléshez vagy a kiértékelési sorrendhez. A valódi körök továbbra is megtalálásra kerülnek; a tévesek eltűntek

Miért engedi meg az Excel, hogy egy keresőtartomány tartalmazza a képletet?

Mert azt az argumentumot nem úgy fogyasztják, ahogyan egy számtani operandust. A keresőcsalád gyorsítótárazott értékekért pásztázza a tartományt, és egyezést ad vissza; nem követeli meg, hogy a tartomány előbb ki legyen értékelve a végéig. Az Excel az önátfedő keresőtartományt úgy kezeli, hogy az olvassa, amit azok a cellák jelenleg tartanak, ami ugyanaz a szemantika, amelyet bármely nem iteratív munkafüzet alkalmaz: azok a cellák, amelyeket ebben a menetben nem számoltak újra, az utolsó kiszámított értéküket adják

A teljes oszlopos hivatkozások ezt gyakori esetté teszik, nem egzotikussá. A B:B az idiomatikus mód annak megírására, hogy „a teljes keresőtábla” egy olyan munkalapon, amelyre sorok kerülnek hozzáfűzésre, és bármely, a B oszlopban élő képlet így a saját keresőtartományán belül van. Pénzügyi modellek, egyeztetési munkalapok és auditmunkafüzetek folyamatosan ezt teszik, általában anélkül, hogy bárki észrevenné az átfedést

A B7 cella VLOOKUP(A1,B:B,1) képletet hordoz a saját teljes oszlopos B:B keresőtartományán belül, olyan önátfedéssel, amelyet az Excel gyorsítótárazott értékekből panasz nélkül számol
A teljes oszlopos keresőtartományok az önátfedést teszik normál esetté pénzügyi modellekben és auditmunkafüzetekben, nem egzotikus zugnak

Mit tesz ugyanazzal a képlettel egy függőséggráf

A HotXLS inkrementálisan számol újra, ami valós függőséggráfot igényel: csomópontokat a celláknak, éleket a hivatkozásoknak, topológiai sorrendet a kiértékeléshez és erősen összefüggő komponensmenetet a körök osztályozására. Ezt a gépezetet a inkrementális újraszámolási cikk írja le, és pontosan ez az ok, amiért a téves pozitív megjelent

A B7 cellában lévő =VLOOKUP(A1,B:B,1) függőségeit kivonva a második argumentum B7-et magát is tartalmazó tartományt ad. A gráf mostantól önhurokkal bír. Annak a csomópontnak a befüggési foka soha nem éri el a nullát, tehát a topológiai menet soha nem ütemezheti, és a komponensmenet körként osztályozza. A motor helyesen következtetett a neki adott gráfról. A gráf a téves modell, mert egy éltípust kódol ott, ahol a táblázat kettőt tud

A B:B keresőtartomány önhurkot ad a B7 gráfcsomópontnak, tehát a befüggési fok soha nem éri el a nullát, és a HotXLS v2.361.98 előtt téves körkörös hivatkozást jelentett
Az újraszámolási motor helyesen következtetett a neki adott gráfról; a gráf volt a téves modell egy táblázathoz

Két él-osztály, egy gráf

A változtatás jelzőt ad a feloldott hivatkozási rekordhoz, a TXLSDepRange.LookupScan-hez, amelyet a függőségkivonó állít be, amikor a hat függvény egyikének keresőtömb argumentumán végigmegy. Lefelé az azokból a hivatkozásokból származó élek a közönséges élektől elkülönítve tárolódnak: a gráfcsomópont ScanDependents és ScanPrecedents listákat tart a szokásos függő és precedens listái mellett

A szétválasztás teszi a szemantikát helyessé. A vizsgálati éleket a szennyezettségterjedés járja be, tehát a B:B bárhol való szerkesztése továbbra is szennyezettnek jelöli a B7-et, és a B7 újraszámol. A vizsgálati élek soha nem számolódnak be a befüggési fokba, és soha nem lépnek be a komponensépítőbe, tehát nem okozhatnak topológiai holtpontot, és nem osztályozhatók körként. A függvénytár mindkét gráfimplementációja, a klasszikus munkafüzetenkénti gráf és a komponenselemzést hordozó munkafüzetek közötti munkaterület-gráf együtt változott meg; hagyva őket szétcsúszni, olyan munkafüzetet kapnánk, amely attól függően számol újra másképp, hogy egyedül vagy munkaterület részeként nyitották meg

A TXLSDepRange.LookupScan vizsgálati élei a szennyezettségterjedést a ScanPrecedents-be és ScanDependents-be hajtják, de soha nem számolódnak be a befüggési fokba vagy a körökbe
A B:B-n belüli szerkesztések továbbra is szennyezettnek jelölik a képletet, mégis a vizsgálati élek nem okozhatnak holtpontot a topológiai menetben, és nem gyárthatnak kört
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;
    // A keresőtartomány lefedi a B oszlopot, és ez a képlet abban él
    Sheet.Cells[7, 2].Formula := 'VLOOKUP(A1,B:B,1)';

    case Book.Recalculate of
      lxOk:
        // v2.361.98 előtt ez az ág elérhetetlen volt ennél a munkalapnál
        SaveReport(Book);
      lxErrorRef:
        LogWarning('Genuine circular reference - review model inputs');
    end;
  finally
    Book.Free;
  end;
end;

Mit ad fel a vizsgálati élek sorrendből való kizárásával

Pontosan egy dolgot, és érdemes nyíltan kimondni, nem elrejteni. Mivel a vizsgálati élek nem vesznek részt a topológiai sorrendben, egy keresőképlet kiértékelhető ugyanabban a menetben, mielőtt a keresőtartományának néhány celláját újraszámolták volna, és ekkor azok előző értékeit olvassa. Az eredmény a következő újraszámoláskor konvergál

Ez elfogadható, mert ez az, amit az Excel tesz. Iteratív számítás nélküli munkafüzetre az Excel saját válasza egy, az aktuális menetben még nem újraszámolt értékre az utolsó kiszámított érték, tehát az a motor, amely ezt a viselkedést reprodukálja, a referencia-implementációhoz illeszkedik, nem közelíti azt. Ha valóban konvergált válaszra van szüksége önmagára hivatkozó modell felett, arra a mechanizmus az explicit iterációs korlátos iteratív számítás, amelyet a iteratív számítási cikk tárgyal, és az valódi körökre vonatkozik, nem vizsgálati átfedésekre

A javítás belsejében rejtőző regressziós veszély

A LookupScan hozzáadása a TXLSDepRange-hez olyan kockázatot vezetett be, amelynek semmi köze a keresőkhöz, és minden köze a Pascalhoz. A TXLSDepRange kezeletlen rekord, tehát az adott típusú lokális változó nem nullaértékű inicializálást kap. A kódbázis minden helye, amely kézzel épít ilyet, beleértve az adattábla-függőségi blokkokat és több tesztsegédet, ezért explicit módon kellett beállítsa az új mezőt. Egyet kihagyni annyit jelent, hogy a veremen véletlenül ülő bájt dönti el, hogy az a hivatkozás vizsgálati élként kezelődik-e, ami olyan újraszámolási hibát produkál, amely idegen kódváltoztatásokkal jelenik meg és tűnik el

// Új logikai mező egy kezeletlen rekordban minden kézi építési
// helyet rejtett hibává tesz. Két biztonságos idióma:
var
  R: TXLSDepRange;
begin
  FillChar(R, SizeOf(R), 0);      // mindent nulláz, aztán tölti
  R.Sheet1 := SheetIndex;
  R.Sheet2 := SheetIndex;
  R.Row1 := Row; R.Col1 := Col;
  R.Row2 := Row; R.Col2 := Col;

  // vagy állítson be minden mezőt, az újjal együtt, minden helyen
  R.LookupScan := False;
end;

A belőle szerzett általános szabály: mező hozzáadása egy olyan rekordhoz, amelyet néhány helynél több helyen a veremen építenek, nagyobb kockázatú változtatás, mint amilyennek látszik, és a fordító nem segít megtalálni a helyeket. Ha a rekord forró útból elérhető, részesítse előnyben azt a segédet, amely teljesen inicializálja, minden hívási hely frissítésébe vetett bizalom helyett

Valódi kör megkülönböztetése a vizsgálati átfedéstől

Semmi ebben a változtatásban nem gyengíti a körészlelést. A =B7+1 a B7-ben továbbra is kör, egy három képletből álló, önmagára záródó lánc továbbra is kör, és mindkettő továbbra is az újraszámolási eredményen keresztül jelentésre kerül, a kör tagjai az előző gyorsítótárazott értékeiket tartva, miközben minden a körön kívüli aktuális marad. Ami megváltozott, az csak az, hogy a keresőtömb argumentum már nem gyárt olyan köröket, amelyeket az Excel nem lát

Ha auditál egy munkafüzetet, és tudni szeretné, mely hivatkozásokat oldott fel valójában a motor, és milyen sorrendben, az értékelési nyomkövető az eszköz rá; a képletértékelési nyomkövető cikk leírja, hogyan olvassa a kimenetét. A HotXLS natív Delphi és C++Builder táblázatkomponens, amely Excel telepítése nélkül olvas és ír XLS, XLSX, ODS és CSV formátumokat, és az újraszámolási motor minden formátumon ugyanaz; az aktuális függvény- és motorlefedettség a HotXLS Delphi spreadsheet component terméklapon található