Tehnični članak

Revizija predpomnilnikov formul Excel s HotXLS Deep Recalc

HotXLS odgovori na vprašanje, ki ga mora vsak preglednični cevovod sčasoma zastaviti: ali se številke, shranjene v delovni knjigi, še ujemajo s formulami, ki so jih proizvedle. CalculateAndVerify preračuna celoten graf odvisnosti v izolirano prekrivanje, vsak rezultat primerja s predpomnjeno vrednostjo, ki je že v celici, in sporoči nesoglasja. Privzeto ne spremeni nič

Razlog, zakaj to šteje, je, da datoteka preglednice shranjuje dve stvari na formulo celice: formulo in zadnjo vrednost, ki jo je kdo zanjo izračunal. Excel ju drži usklajena. Vse ostalo na svetu pa morda ne. Datoteka, ki je šla skozi starejšo knjižnico, delno preračunanje, ročno urejen del XML ali orodje, ki je zapisalo vrednosti brez preračuna, bo z veseljem prikazala vsoto, ki iz njenih vhodov ne sledi več — in nič v formatu datoteke tega ne označi

Zakaj je predpomnjena vrednost, ki se ne ujema s formulo, tako nevarna?

Ker je nevidna na vsaki navadni bralni poti. Datoteko odprete v pregledovalniku, celico preberete skozi API, izvozite v CSV ali PDF — in dobite predpomnjeno številko. Formula je prav tam v isti celici in nihče jih ne primerja. Neskladje pride na dan šele, ko nekdo odpre delovno knjigo v Excelu, ki se ob večini nastavitev ob nalaganju preračuna, in naenkrat poročilo, odobreno lansko četrtletje, prikazuje drugačne vsote

Revizija obstaja zato, da je ta primerjava premišljena, načrtovana operacija in ne nesreča. To je preglednična ustreznica preverjanja kontrolne vsote: dovolj poceni, da teče v sprejemnem cevovodu, in edina stvar, ki tiho problem celovitosti podatkov spremeni v poročilo, na katerem lahko ukrepate

var
  Book: TXLSWorkbook;
  Options: TXLSRecalcAuditOptions;
  Report: TXLSCalculationAuditReport;
  I: Integer;
begin
  Book := TXLSWorkbook.Create(nil);
  try
    Book.LoadFromFile('quarterly-close.xls');
    Options := TXLSRecalcAuditOptions.Default;
    Options.MaxIssues := 500;
    Report := Book.CalculateAndVerify(Options);
    try
      for I := 0 to Report.Count - 1 do
        if Report[I].Kind = xlcaiCacheMismatch then
          Writeln(Report[I].SheetName, '!',
                  Report[I].Row, ':', Report[I].Col, '  ',
                  Report[I].Formula,
                  '  cached=', VarToStr(Report[I].Actual),
                  '  recomputed=', VarToStr(Report[I].Expected));
      if Report.Truncated then
        Writeln('issue budget reached, raise MaxIssues');
    finally
      Report.Free;
    end;
  finally
    Book.Free;
  end;
end;

Obstajajo tri preobremenitve in odgovarjajo na tri različna vprašanja. CalculateAndVerify brez parametrov vrne število neskladij, kar je vse, kar potrebuje zdravstveni pregled. Preobremenitev z out seznamom neskladij vam da celice. Preobremenitev, ki sprejme TXLSRecalcAuditOptions, vrne poln TXLSCalculationAuditReport — tista, po kateri segate, ko morate vedeti ne le, da se vrednost ne ujema, ampak tudi zakaj revizija česa ni mogla ovrednotiti

Prekrivanje in zakaj revizija ne piše

Vsaka preračunana vrednost pristane v prekrivanju in ne v predpomnilniku celic, prekrivanje pa je vbrizgano na samem začetku povratnega klica branja celic v obeh motorjih delovnih knjig. Ta umestitev naredi revizijo samoskladno: ko se B1 preračuna in C1 visi od B1, C1 vidi vrednost iz tega revizijskega prehoda in ne zastarele predpomnjene. Brez tega bi se ena sama napaka na zgornjem toku sporočila enkrat in se nato absorbirala, vsaka celica na spodnjem toku pa bi se zdelo, da se strinja z napačnim vhodom

Celice, katerih preračunana vrednost se ujema s predpomnilnikom, v prekrivanje sploh ne zaidejo. To ni mikro-optimizacija, to je tisto, kar revizijo naredi plačljivo. Čista delovna knjiga s sto tisoč formulami izvede nič pisanij v prekrivanje in prehod ostane znotraj proračuna 1.35-krat proti polnemu preračunu — razlika med stvarjo, ki jo lahko poganjate na vsakem sprejemu, in stvarjo, ki jo poganjate enkrat na četrtletje

Cevovod revizije globokega preračuna HotXLS: delovna knjiga se naloži s predpomnilniki nedotaknjenimi, vsako vozlišče odvisnosti se označi umazano in ovrednoti enkrat v topološkem vrstnem redu, preračunane vrednosti pristanejo v izoliranem prekrivanju, ki ga povratni klic branja celic v obeh motorjih poizprasha prvega, rezultati se primerjajo s predpomnjenimi vrednostmi, razvrstijo skozi CalculateAndVerify v TXLSCalculationAuditReport, na disk pa se ne zapiše nič
Preračunane vrednosti pristanejo v prekrivanju pred povratnim klicem branja celic, se ujemajoče celice ga nikoli ne dotaknejo, delovna knjiga na disku pa ostane nedotaknjena, razen če ApplyResults potrdi popolnoma čist prehod

Vrednotenje sledi serijskemu topološkemu vrstnemu redu, izpeljanemu iz grafa odvisnosti, vsako vozlišče najprej označeno kot umazano, tako da se vsaka celica izračuna točno enkrat po svojih vhodih. Če želite inkrementalno mehaniko, ki živo delovno knjigo drži aktualno, namesto da bi revizirali shranjeno, je to drugačen mehanizem, opisan v inkrementalnem preračunu in grafu odvisnosti

Spodleteli so razvrščeni, ne zgrntani skupaj

Celica, ki je revizija ne more ovrednotiti, ni isto odkritje kot celica, katere vrednost se ne ujema, TXLSCalculationAuditIssueKind pa drži kategorije narazen. xlcaiCacheMismatch je neskladje vrednosti. xlcaiMissingFunction in xlcaiMissingName pravita, da je vrednotitelj srečal nekaj, česar ne implementira ali ne more razrešiti. xlcaiUnsupportedArguments pokriva oblike argumentov izven podprte podmnožice. xlcaiExternalReferenceDenied in xlcaiExternalReferenceMissing ločita politično zavrnitev od odsotne delovne knjige. xlcaiCircularReference, xlcaiDataTableSkipped, xlcaiParseFailure, xlcaiCancelled in xlcaiInternalFailure dopolnijo nabor

Razvrščanje revizijskih odkritij HotXLS: TXLSCalculationAuditIssueKind loči neskladje vrednosti, sporočeno kot xlcaiCacheMismatch, od vrst spodletelega vrednotenja, kot so xlcaiMissingFunction, xlcaiMissingName, xlcaiUnsupportedArguments, par xlcaiExternalReferenceDenied proti xlcaiExternalReferenceMissing in xlcaiCircularReference, pozitivna koda napake Excel pa šteje kot rezultat in ne kot spodletel
Ena vrsta sporoča neskladje vrednosti, ostale pa sporočajo, zakaj vrednotitelj celice ni mogel oceniti; vrednost napake Excel je izračunan rezultat, zato namerne celice napak proizvedejo nič odkritij

Ena razlika si zasluži, da se izreče, ker obrne pogosto predpostavko. Pozitivna koda napake Excel je rezultat, ne spodletel. Celica, ki legitimno ovrednoti na #DIV/0!, se je izračunala pravilno, zato revizija to napako shrani v prekrivanje in jo primerja s predpomnilnikom kot katero koli drugo vrednost. Delovna knjiga polna namernih celic napak proizvede nič odkritij, delovna knjiga, kjer se je napaka pojavila ali izginila, odkar so bile vrednosti predpomnjene, pa proizvede točno tista odkritja, ki jih želite

Krožni sklici dobijo svojo obravnavo. Vozlišča v ciklu nikoli ne zaidejo v topološki vrstni red, zato se vsako sporoči posamično kot xlcaiCircularReference, revizija pa ne poganja iterativnega reševalnika. To je namerna pogodba samo-za-branje: ali je iteracija vklopljena, vpliva na to, kako naj se koda rezultata razlaga, ne na to, kaj revizija stori. Mehanika iterativnega vrednotenja je obravnavana ločeno v iterativnem računanju in krožnih sklicih

Branje verige spodletelih

Ko formula spodleti pri vrednotenju, je vedeti, katera celica je spodletela, redko dovolj, ker je spodletel običajno tri ravni navzdol po verigi sklicev. Vsako odkritje zato nosi niz Stack, izrisan z najbolj zunanjim okvirjem najprej, v obliki Sheet1!A1 > Sheet1!B2 > Data!C7, tako da poročilo kaže na celico, ki je dejansko počila, in ne na celico, na katero ste slučajno pogledali

Snemalnik je omejen. MaxStackFrames je privzeto 64 s tlemi 8 in najgloblja veriga spodletelih je tista, ki se obdrži: notranji okvir zabeleži verigo, ko tam izvira spodletel, zunanji okvirji, ki se odvijajo kasneje, je ne prepišejo. Če je katera koli veriga presegla proračun, se nastavi Report.StackTruncated, kar vam pove razliko med kratko verigo in verigo, katere vsega niste videli

Veriga spodletelih revizije HotXLS: ko formula tri sklice navzdol spodleti, Stack izriše najbolj zunanji okvir najprej — Sheet1!A1, nato Sheet1!B2, nato Data!C7 — najbolj notranji okvir zabeleži verigo in se odvijajoči zunanji okvirji je ne prepišejo, MaxStackFrames je privzeto 64 s tlemi 8, Report.StackTruncated pa označi verigo, katere vsega niste videli
Stack izriše najbolj zunanji okvir najprej, tako da poročilo kaže na celico, ki je dejansko počila, najgloblja veriga spodletelih je tista, ki se obdrži, StackTruncated pa loči kratke verige od odrezanih
// Privzeto samo za branje. ApplyResults potrdi prekrivanje šele po
// popolnoma uspešni reviziji, pod pisalnim stražarjem, ki potrditev
// zavrne, če se struktura delovne knjige med revizijo spremeni
Options := TXLSRecalcAuditOptions.Default;
Options.ApplyResults := True;
Options.AbsoluteTolerance := 0;     // točna primerjava, razkrije zdrsnitev
Options.RelativeTolerance := 0;
Options.OnProgress := HandleProgress;

Report := Book.CalculateAndVerify(Options);
try
  if Report.Applied then
    Book.SaveToFile('quarterly-close-repaired.xls')
  else
    Writeln('not applied: ', Report.Count, ' issues blocked the commit');
finally
  Report.Free;
end;

procedure THarness.HandleProgress(ASender: TObject;
  ACurrent, ATotal: Integer; var ACancel: Boolean);
begin
  ACancel := FUserRequestedStop;   // revizija se ustavi na naslednji meji vozlišč
end;

Kdaj naj reviziji dovolite popraviti delovno knjigo?

Šele, ko je revizija prišla nazaj popolnoma čista vrst spodletelih — natanko pogoj, ki vam ga uveljavi ApplyResults. Potrditev se zgodi po popolnoma uspešnem prehodu, ki ni bil preklican, in prestane strukturni stražar: binarni motor opazuje identifikator spremembe delovne knjige, motor OOXML posname generacijo strukture na posamezen delovni list. Če se je karkoli premaknilo, medtem ko je revizija tekla, rezultati opisujejo delovno knjigo, ki ne obstaja več, in potrditev je zavrnjena

Opazite namerno asimetrijo. Neskladja predpomnilnika ne blokirajo uporabe, ker so natanko tisto, kar je potrditev tu, da popravi. Vrste spodletelih jo blokirajo, ker bi bila delovna knjiga, kjer nekaterih formul ni bilo mogoče ovrednotiti, pol popravljena — pol popravljena delovna knjiga pa je slabša od nepopravljene, za katero veste, da ji ne zaupate

Toleranca je odločitev politike, ne privzetek

Privzeta primerjava je absolutna toleranca 1E-6 z izključeno relativno toleranco, kar ohranja klasično vedenje in tiho sprejme zdrsnitev 4E-7. To je običajno prav: razlike v vrstnem redu plavajočega vrednotenja med tistim, kar je datoteko proizvedlo, in trenutnim vrednotiteljem bodo na dolgih vsotah dale razlike te velikosti, njihovo sporočanje kot odkritij celovitosti pa je šum

Obe toleranci nastavite na nič, ko je vprašanje drugačno — ko poskušate izvedeti, ali je vrednotitelj med verzijami spremenil vedenje, ali pa tretjeosebno orodje prepisuje vrednosti na subtilno drugačen način. Pri nič isti zdrsnitev 4E-7 postane vidna in vse ostalo tudi. Toleranco izberite glede na vprašanje, ki ga zastavljate, izbiro pa zabeležite poleg poročila, ker poročilo brez svoje tolerance ni razumljivo

Dve sosednji zmožnosti dopolnita sliko. Ko želite vedeti, zakaj ena sama formula proizvede vrednost, ki jo proizvaja, je pravo orodje pogled korak-za-korakom v sledilniku vrednotenja formul. Ko namerno želite, da se predpomnjene vrednosti spoštujejo brez kakršnega preračuna — na primer na sprejemni poti, ki mora datoteko reproducirati točno takšno, kot je prispela — je ta način opisan v brananju predpomnjenih vrednosti formul brez preračuna. Revizija je tisto, kar sede med njima: pove vam, ali je zaupanje predpomnilniku varno. Pride z HotXLS Delphi spreadsheet component za oba motorja, binarnega in OOXML