HotXLS, det opprinnelige Excel-biblioteket for Delphi og C++Builder, utfører inkrementell formel-rekalkulering gjennom TXLSXWorkbook.Recalculate. Det første kallet bygger en formel-avhengighetsgraf og evaluerer hver formelcelle; hvert påfølgende kall reevaluerer kun cellene som er påvirket av verdi-skriving siden forrige kjøring, i topologisk rekkefølge, i ett enkelt sveip der kostnaden er proporsjonal med antall skitne celler snarere enn størrelsen på arbeidsboken
Den ene designbeslutningen er forskjellen mellom en økonomisk modell som reagerer på en endret forutsetning på millisekunder, og en som stopper opp i sekunder. Hvis du genererer rapporter der en håndfull inndataceller mater tusenvis av etterfølgende formler, forklarer resten av denne artikkelen hva grafen gjør, hvilke funksjoner som velger bort inkrementalitet, og hvordan sirkulære referanser rapporteres i stedet for å gå i uendelig løkke
Hvorfor vil endring av én celle rekalkulere hundre tusen formler?
En naiv formelmotor har ikke minne om hvem som avhenger av hvem, så dens eneste trygge trekk etter en redigering er å evaluere alt på nytt. Enda verre er det at den klassiske rekursive strategien — når formel A refererer til formel B, evaluer B på stedet — reevaluerer refererte celler ubetinget, og ignorerer eventuelle bufrede verdier. En kjede med n formler der hver refererer til den forrige koster O(n²) evalueringer per full kjøring, og en sirkulær referanse sender rekursjonen utenfor stupet. Enhver regnearkutvikler som har koblet en kaskademodell til en rekursiv evaluator har sett begge feilmodusene inntreffe
Excel selv løste dette for flere tiår siden med sin beregningskjede: en sortering av formelceller vedlikeholdes slik at en redigering markerer et lite sett med celler som skitne, og motoren går bare gjennom den berørte halen av kjeden. HotXLS bruker samme idé som en eksplisitt avhengighetsgraf, bygd én gang fra de kompilerte formeltrærne og gjenbrukt på tvers av rekalkuleringskjøringer. Poenget er ikke smarthet; det er at rekalkuleringskostnaden skal følge størrelsen på redigeringen din, ikke størrelsen på arbeidsboken din
Hvordan avhengighetsgrafen gjør en redigering to en enkelt kjøring
HotXLS-avhengighetsgrafen gir hver formelcelle én node, med kanter (edges) som går fra forløper (precedent) til avhengig (dependent). Når koden dein skriver en celleverdi, registrerer arbeidsboken cellen som skitten; når Recalculate kjører, forplanter skittenheten seg langs kantene til hver etterfølgende formel, og den skitne delgrafen evalueres nøyaktig én gang i topologisk rekkefølge ved hjelp av Kahns algoritme. Fordi en formel aldri besøkes før sine forløpere, trenger hver node en enkelt evaluering — det er det som gjør kjøringen O(skitten)
Topologisk rekkefølge løser også rekursjonsproblemet ved roten. Under en rekalkuleringskjøring bytter motoren til en dedikert modus der enhver referanse to en annen formelcelle leser den cellens bufrede verdi direkte i stedet for å reevaluere den — sorteringen garanterer at bufferen allerede er fersk. Den samme mekanismen gjør at en referansesyklus ikke kan utløse ubegrenset rekursjon: ingenting i kjøringen går noen gang inn i evaluatoren for en nabocelle igjen
var
Book: TXLSXWorkbook;
Inputs, Model: TXLSXWorksheet;
begin
Book := TXLSXWorkbook.Create;
try
Inputs := Book.Sheets.Add('Inputs');
Model := Book.Sheets.Add('Model');
Inputs.Cells[2, 2].Value := 0.05; // growth assumption
Model.Cells[2, 2].Formula := 'Inputs!B2*1000'; // XLSX formulas take no leading '='
Model.Cells[3, 2].Formula := 'B2*(1+Inputs!B2)';
// ... thousands more rows cascading off the same assumption ...
Book.Recalculate; // first call: builds the graph, full evaluation
Inputs.Cells[2, 2].Value := 0.07; // one edit marks one cell dirty
Book.Recalculate; // second call: only the downstream chain runs
finally
Book.Free;
end;
end;
Hver verdi lander i cellens bufrede Value, slik at etter Recalculate returnerer, kan du lese utdataene på samme måte som du leser alle andre celler. I en rapportgenereringsløkke er mønsteret akkurat som koden over: last inn eller bygg modellen én gang, og veksle deretter mellom å skrive til noen få inndataceller og kalle Recalculate, slik at du bare betaler for formlene som faktisk avhenger av det som ble endret
Hvilke Excel-funksjoner tvinger frem rekalkulering ved hver kjøring?
HotXLS behandler NOW, TODAY, RAND, OFFSET og INDIRECT som volatile: enhver formel som inneholder en av dem, reevalueres ved hver Recalculate-kjøring, uavhengig av om noe oppstrøms har endret seg. De tre første er volatile av samme grunn som de er i Excel — resultatet deres avhenger av evalueringstidspunktet, ikke av andre celler. OFFSET og INDIRECT er volatile av en mer subtil grunn: cellene de leser beregnes ved kjøring, så grafen kan ikke vite statisk hvilke kanter den skal tegne for dem
Den samme konservative regelen gjelder for referanser som grafbyggeren ikke kan feste til et enkelt rektangel. En formel som går gjennom et navngitt område med flere delområder, eller en som refererer til en ekstern arbeidsbok, blir på samme måte degradert til volatil og reevaluert ved hver kjøring. Retningslinjen er tilsiktet: en ekstra evaluering koster litt tid, men en manglende avhengighetskant betyr en lydløst utdatert verdi i en sendt rapport, og det er en langt verre feil. Hvis modellen din støtter seg på navngitte områder med arbeidsbok-omfang, dekker ledsagerartikkelen om definerte navn og formler på tvers av ark hvordan navn med ett område løses opp — disse deltar i grafen på normal måte
Den praktiske veiledningen følger direkte. Hold kritiske baner i en stor modell på enkle celle- og områdereferanser der grafen kan gjøre jobben sin, og sett OFFSET og INDIRECT i karantene til de få stedene som faktisk trenger dynamisk adressering. En modell med tusen volatile formler kjører disse tusen på nytt ved hver kjøring uansett hvor liten redigeringen var — akkurat den oppførselen Excel-brukere kjenner fra arbeidsbøker som "rekalkulerer ved hvert tastetrykk"
Hvordan rapporterer HotXLS sirkulære referanser?
Hvorfor vil endring av én celle rekalkulere hundre tusen formler?
TXLSXWorkbook.Recalculate returnerer lxOk ved en ren kjøring og lxErrorRef når den oppdager en referansesyklus. Syklusmedlemmer identifiseres under den topologiske sorteringen — de er nodene Kahns algoritme aldri kan frigi — og de hoppes over i stedet for å gå i løkke: deres bufrede verdier forblir det de var, mens hver formel utenfor syklusen fortsatt evalueres normalt i rekkefølge. Ditt kallsted får en bestemt feilkode i stedet for å henge
case Book.Recalculate of
lxOk:
SaveReport(Book);
lxErrorRef:
// a reference cycle exists; cycle members kept their previous
// cached values and everything outside the cycle is up to date
LogWarning('Circular reference detected - review model inputs');
end;
Å finne hvilke celler som danner syklusen er en feilsøkingsjobb, og formelevalueringstraceren er det rette verktøyet for det: spor den mistenkte formelen, og referansekjeden som bretter seg tilbake på seg selv blir synlig trinn for trinn. Sykluser i virkelige modeller er nesten alltid en forfatterfeil — en oppsummeringsrad som ved et uhell er inkludert i sitt eget SUM-område — så en tydelig feilkode ved rekalkuleringstidspunktet er akkurat det du vil ha
Matriseformler, skitten-sporing, og når grafen bygges på nytt
CSE-matriseformler får én node for hele det forankrede rektangelet, ikke én node per celle. Rotformelen evalueres én gang per kjøring; den resulterende matrisen skrives direkte inn i hver medlems-celle, og en formel som refererer til en celle i det forankrede området — ikke bare forankringen øverst til venstre — plukker opp en avhengighetskant fra den rotnoden. Skalare resultater spres over rektangelet slik Excels eldre matrise-semantikk foreskriver
Skitten-sporing hekter seg på de vanlige egenskaps-setterne, så ingenting endres i koden din. Skriving av Value på en celle varsler arbeidsboken og markerer avhengige som skitne; tildeling av en ny Formula er en strukturell endring, så det markerer hele grafen som foreldet, og neste Recalculate bygger den på nytt før evaluering. Å legge til, slette eller flytte ark ugyldiggjør også grafen, siden nodeidentiteten koder arkindeksen. Når ingen graf er aktiv — en arbeidsbok du aldri kaller Recalculate på — koster hektene en enkelt nil-sjekk per tildeling, så vanlige lese- og skrivearbeidsmengder påvirkes ikke
En grense det er verdt å nevne ærlig: grafen sporer avhengigheter mellom celler, så en brukerdefinert funksjon registrert via OnUserFunction reevalueres når cellene som mater argumentene endres, akkurat som enhver annen formel. Hvis du utvider motoren på den måten, går artikkelen om egendefinerte funksjoner i HotXLS-formelmotoren gjennom tilbakeslagskontrakten (callback contract) og hvordan argumentverdier ankommer
Inkrementell rekalkulering er en del av den standard XLSX-motoren i HotXLS Delphi Excel Component, sammen med formelkalkulatoren, definerte navn og import/eksport-pipelinen den akselererer. Hvis din Delphi- eller C++Builder-applikasjon vedlikeholder levende modeller — prisark, konsolideringsarbeidsbøker, rapportkaskader — er Recalculate forskjellen mellom å beregne en arbeidsbok på nytt og å beregne en redigering på nytt