Teknisk artikkel

HotXLS oppslagsskann og falske sirkulære referanser

Sett =VLOOKUP(A1,B:B,1) i en celle i kolonne B, og Excel beregner den uten klage. Mater den samme arbeidsboken til en avhengighetsgraf-omberegningsmotor, får du sannsynligvis en sirkulær referansefeil, fordi formelen avhenger av et område som inneholder formelen. HotXLS rapporterte nøyaktig det til v2.361.98. Fiksen er ikke et spesialtilfelle for hele-kolonne-områder; den er et skille mellom to slags avhengighetskant som en regnearkmotor trenger og en enkel rettet graf ikke har

Lookup-array-argumentet til oppslagsfamilien, LOOKUP, MATCH, HLOOKUP, VLOOKUP, XLOOKUP og XMATCH, er nå merket som en skannreferanse. En skannreferanse såer fortsatt skittenshet, så å redigere en celle inne i området beregner formelen på nytt, men den bidrar aldri til syklusedeteksjon eller til evalueringsordening. Ekte sykluser finnes fortsatt; de falske er borte

Hvorfor tillater Excel at et oppslagsområde inneholder formelen?

Fordi det argumentet ikke konsumeres slik en aritmetisk operand er. Oppslagsfamilien skanner området for bufrede verdier og returnerer et treff; den krever ikke at området er evaluert til fullførelse først. Excel behandler et selv-overlappende oppslagsområde som å lese hva de cellene for øyeblikket holder, noe som er den samme semantikken den anvender på enhver ikke-iterativ arbeidsbok: celler som ikke er beregnet på nytt i denne gjennomkjøringen, bidrar med sin sist beregnede verdi

Hele-kolonne-referanser gjør dette til det vanlige tilfellet snarere enn et eksotisk. B:B er den idiome måten å skrive «hele oppslagstabellen» på i et ark der rader appenderes, og enhver formel som bor i kolonne B, er da inne i sitt eget oppslagsområde. Finansielle modeller, avstemmingsark og revisjonsarbeidsbøker gjør dette stadig, vanligvis uten at noen legger merke til at området overlapper

Cellen B7 holder VLOOKUP(A1,B:B,1) inne i sitt eget hele-kolonne-oppslagsområde B:B, en selv-overlapp Excel beregner fra bufrede verdier uten klage
Hele-kolonne-oppslagsområder gjør selv-overlapp til det normale tilfellet i finansielle modeller og revisjonsarbeidsbøker, ikke et eksotisk hjørne

Hva en avhengighetsgraf gjør med den samme formelen

HotXLS beregner inkrementelt på nytt, noe som krever en ekte avhengighetsgraf: noder for celler, kanter for referanser, en topologisk ordening for evaluering og en sterkt-sammenkoblet-komponent-passering for å klassifisere sykluser. Det maskineriet er beskrevet i artikkelen om inkrementell omberegning, og det er presist hvorfor den falske positive oppsto

Trekk ut avhengigheter fra =VLOOKUP(A1,B:B,1) i cellen B7, og det andre argumentet gir et område som inneholder B7 selv. Grafen har nå en selvsløyfe. In-graden av den noden når aldri null, så den topologiske passeringen kan aldri planlegge den, og komponentpasseringen klassifiserer den som en syklus. Motoren resonerer korrekt om grafen den fikk. Grafen er feil modell, fordi den koder én kanttype der regnearket har to

B:B-oppslagsområdet gir grafnoden B7 en selvsløyfe, så in-graden når aldri null og HotXLS før v2.361.98 rapporterte en falsk sirkulær referanse
Omberegningsmotoren resonnerte korrekt om grafen den fikk; grafen var feil modell for et regneark

To kantklasser, én graf

Endringen legger et flagg til den løste referanserecorden, TXLSDepRange.LookupScan, som avhengighetsekstraktoren setter når den går gjennom lookup-array-argumentet til én av de seks funksjonene. Nedstrøms lagres kanter med kilde i de referansene adskilt fra ordinære kanter: grafnoden beholder ScanDependents- og ScanPrecedents-lister ved siden av sine normale dependent- og precedent-lister

Skillelinjen er det som gjør semantikken riktig. Skannkanter traverseres av skitten-propagering, så en redigering hvor som helst i B:B markerer fortsatt B7 som skitten, og B7 beregnes på nytt. Skannkanter telles aldri inn i in-graden og går aldri inn i komponentbyggeren, så de kan ikke skape en topologisk vranglås og kan ikke klassifiseres som en syklus. Begge grafimplementeringene i biblioteket, den klassiske per-arbeidsbok-grafen og tvers-arbeidsbok-arbeidsområdegrafen som bærer komponentanalysen, ble endret sammen; å la dem drive ville produsere en arbeidsbok som beregnes annerledes avhengig av om den ble åpnet alene eller som del av et arbeidsområde

Skannkanter fra TXLSDepRange.LookupScan driver skitten-propagering inn i ScanPrecedents og ScanDependents men teller aldri inn i in-graden eller sykluser
Redigeringer inne i B:B markerer fortsatt formelen skitten, men skannkanter kan ikke vranglåse den topologiske passeringen eller fabrisere en syklus
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;
    // Oppslagsområdet dekker kolonne B, og denne formelen bor i den
    Sheet.Cells[7, 2].Formula := 'VLOOKUP(A1,B:B,1)';

    case Book.Recalculate of
      lxOk:
        // Før v2.361.98 var denne grenen uoppnåelig for dette arket
        SaveReport(Book);
      lxErrorRef:
        LogWarning('Genuine circular reference - review model inputs');
    end;
  finally
    Book.Free;
  end;
end;

Hva du gir avkall på ved å ekskludere skannkanter fra ordeningen

Nøyaktig én ting, og det er verdt å si klart snarere enn å gjemme. Fordi skannkanter ikke deltar i den topologiske ordeningen, kan en oppslagsformel evalueres i samme passering før noen celler i sitt oppslagsområde er beregnet på nytt, og den leser da deres tidligere verdier. Resultatet konvergerer ved neste omberegning

Det er akseptabelt fordi det er hva Excel gjør. For en arbeidsbok uten iterativ beregning aktivert, er Excels eget svar på en verdi ennå ikke beregnet i den nåværende passeringen den sist beregnede verdien, så en motor som reproduserer denne atferden, matcher referanseimplementeringen snarere enn å tilnærme den. Hvis du trenger et genuint konvergert svar over en selv-referensiell modell, er mekanismen for det iterativ beregning med en eksplisitt iterasjonsgrense, dekket i artikkelen om iterativ beregning, og den gjelder ekte sykluser snarere enn skannoverlapp

Regresjonsfaren som gjemmer seg inne i fiksen

Å legge LookupScan til TXLSDepRange innførte en risiko som ikke har noe med oppslag å gjøre og alt med Pascal. TXLSDepRange er en uadministrert record, så en lokal variabel av den typen er ikke null-initialisert. Hvert sted i kodebasen som bygger én for hånd, inkludert datatabell-avhengighetsblokkene og flere testhjelpere, måtte derfor oppdateres til å sette det nye feltet eksplisitt. Miss én, og den byten som tilfeldigvis var på stakken avgjør om den referansen behandles som en skannkant, noe som produserer en omberegningsfeil som opptrer og forsvinner med urelatte kodeendringer

// Et nytt Boolean-felt i en uadministrert record gjør hvert manuelle
// konstruksjonssted til en latent feil. To trygge idiomer:
var
  R: TXLSDepRange;
begin
  FillChar(R, SizeOf(R), 0);      // nullstill alt, og fyll deretter inn
  R.Sheet1 := SheetIndex;
  R.Sheet2 := SheetIndex;
  R.Row1 := Row; R.Col1 := Col;
  R.Row2 := Row; R.Col2 := Col;

  // eller sett hvert felt, inkludert det nye, på hvert sted
  R.LookupScan := False;
end;

Den generelle regelen dette tjente: å legge et felt til en record som konstrueres på stakken på mer enn en håndfull steder, er en høyrisikoendring sammenlignet med hvordan den ser ut, og kompilatoren vil ikke hjelpe deg å finne stedene. Hvis recorden er nåbar fra en varm vei, foretrekk en hjelper som initialiserer den fullstendig fremfor å stole på at hvert kallsted oppdateres

Å skille en ekte syklus fra en skannoverlapp

Ingenting ved denne endringen svekker syklusedeteksjon. =B7+1 i B7 er fortsatt en syklus, en kjede av tre formler som lukker seg på seg selv er fortsatt en syklus, og begge rapporteres fortsatt gjennom omberegningsresultatet med syklusmedlemmene som beholder sine tidligere bufrede verdier mens alt utenfor syklusen forblir gjeldende. Det som endret seg, er bare at lookup-array-argumentet ikke lenger fabriserer sykluser Excel ikke ser

Hvis du reviderer en arbeidsbok og vil vite hvilke referanser motoren faktisk løste og i hvilken rekkefølge, er evalueringssporeren verktøyet for det; artikkelen om formelevalueringssporeren dekker hvordan du leser outputen. HotXLS er en nativ Delphi- og C++Builder-regnearkkomponent som leser og skriver XLS, XLSX, ODS og CSV uten Excel installert, og omberegningsmotoren er den samme på hvert format; nåværende funksjons- og motordekning er oppført på produktsiden for HotXLS Delphi spreadsheet component