Tehnički članak

Pretraživanja u HotXLS-u i lažne kružne reference

Stavite =VLOOKUP(A1,B:B,1) u ćeliju u stupcu B i Excel to izračunava bez zamjerke. Dajte istu radnu knjigu motoru ponovnog izračuna grafa ovisnosti i vjerojatno ćete dobiti pogrešku kružne reference, jer formula ovisi o rasponu koji sadrži formulu. HotXLS je javljao upravo to sve do v2.361.98. Ispravak nije poseban slučaj za raspone cijelih stupaca; to je razlika između dviju vrsta ruba ovisnosti koje motor proračunske tablice treba, a običan usmjereni graf nema

Argument niza pretraživanja obitelji pretraživanja, LOOKUP, MATCH, HLOOKUP, VLOOKUP, XLOOKUP i XMATCH, sada je označen kao referenca skeniranja. Referenca skeniranja i dalje sije nečistoću, pa uređivanje ćelije unutar raspona ponovno izračunava formulu, ali nikad ne pridonosi otkrivanju ciklusa ni poretku evaluacije. Stvarni ciklusi i dalje se nalaze; lažni su nestali

Zašto Excel dopušta da raspon pretraživanja sadrži formulu?

Zato se taj argument ne troši načinom na koji se troši aritmetički operand. Obitelj pretraživanja skenira raspon za predmemoriranim vrijednostima i vraća podudaranje; ne zahtijeva da raspon bude prvo evaluiran do kraja. Excel tretira samopreklapajući raspon pretraživanja kao čitanje onoga što te ćelije trenutno drže, što je ista semantika koju primjenjuje na svaku radnu knjigu bez iterativnog izračuna: ćelije koje nisu ponovno izračunate u ovom prolazu pridonose svoju posljednju izračunatu vrijednost

Reference cijelih stupaca čine ovo uobičajenim slučajem, a ne egzotičnim. B:B idiomatski je način pisanja "cijele tablice pretraživanja" u listu gdje se retci dodaju, i svaka formula koja živi u stupcu B tada je unutar vlastitog raspona pretraživanja. Financijski modeli, listovi usklađivanja i revizijske radne knjige to rade stalno, obično bez da itko primijeti preklapanje raspona

Ćelija B7 drži VLOOKUP(A1,B:B,1) unutar vlastitog raspona pretraživanja cijelog stupca B:B, samopreklapanje koje Excel izračunava iz predmemoriranih vrijednosti bez zamjerke
Rasponi pretraživanja cijelih stupaca čine samopreklapanje uobičajenim slučajem u financijskim modelima i revizijskim radnim knjigama, a ne egzotičnim kutom

Što graf ovisnosti čini s istom formulom

HotXLS ponovno izračunava inkrementalno, što zahtijeva stvarni graf ovisnosti: čvorove za ćelije, rubove za reference, topološki poredak za evaluaciju i prolaz snažno povezanih komponenti za klasificiranje ciklusa. Ta je mehanika opisana u članku o inkrementalnom ponovnom izračunu, i to je upravo razlog zbog kojeg se pojavila lažna pozitivnost

Izvucite ovisnosti iz =VLOOKUP(A1,B:B,1) u ćeliji B7 i drugi argument daje raspon koji sadrži sam B7. Graf sada ima petlju na sebi. Ulazni stupanj tog čvora nikad ne dođe do nule, pa topološki prolaz nikad ne može rasporediti taj čvor, a prolaz komponenti klasificira ga kao ciklus. Motor ispravno zaključuje o grafu koji je dobio. Graf je pogrešan model, jer kodira jednu vrstu ruba gdje proračunska tablica ima dvije

Raspon pretraživanja B:B daje čvoru grafa B7 petlju na sebi, pa ulazni stupanj nikad ne dođe do nule i HotXLS prije v2.361.98 javljao je lažnu kružnu referencu
Motor ponovnog izračuna ispravno je zaključivao o grafu koji je dobio; graf bio je pogrešan model za proračunsku tablicu

Dva razreda rubova, jedan graf

Promjena dodaje zastavicu zapisu razriješene reference, TXLSDepRange.LookupScan, koju izvlačitelj ovisnosti postavlja kada prelazi argument niza pretraživanja jedne od šest funkcija. Nizvodno, rubovi s tim referencama kao izvorom spremaju se odvojeno od običnih rubova: čvor grafa drži popise ScanDependents i ScanPrecedents uz svoje uobičajene popise ovisnih i prethodnih

Odvojenost je ono što semantiku čini ispravnom. Rubovi skeniranja prelaze se propagacijom nečistoće, pa uređivanje bilo gdje u B:B i dalje označava B7 nečistim i B7 se ponovno izračunava. Rubovi skeniranja nikad se ne računaju u ulazni stupanj i nikad ne ulaze u graditelj komponenti, pa ne mogu stvoriti topološko zastoj i ne mogu se klasificirati kao ciklus. Obje implementacije grafa u knjižnici, klasični graf po radnoj knjizi i graf radnog prostora između radnih knjiga koji nosi analizu komponenti, promijenjeni su zajedno; dopustiti im da odstanu proizvelo bi radnu knjigu koja se ponovno izračunava različito ovisno o tome je li otvorena sama ili kao dio radnog prostora

Rubovi skeniranja iz TXLSDepRange.LookupScan pokreću propagaciju nečistoće u ScanPrecedents i ScanDependents, ali nikad se ne računaju u ulazni stupanj ili cikluse
Uređivanja unutar B:B i dalje označavaju formulu nečistom, ali rubovi skeniranja ne mogu zastojati topološki prolaz ni proizvesti ciklus
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;
    // Raspon pretraživanja pokriva stupac B, i ova formula u njemu živi
    Sheet.Cells[7, 2].Formula := 'VLOOKUP(A1,B:B,1)';

    case Book.Recalculate of
      lxOk:
        // Prije v2.361.98 ova grana bila je nedostižna za ovaj list
        SaveReport(Book);
      lxErrorRef:
        LogWarning('Genuine circular reference - review model inputs');
    end;
  finally
    Book.Free;
  end;
end;

Što odustajete isključenjem rubova skeniranja iz poredka

Točno jednu stvar, i vrijedi je izreći otvoreno umjesto sakriti. Budući da rubovi skeniranja ne sudjeluju u topološkom poretku, formula pretraživanja može se evaluirati u istom prolazu prije nego su neke ćelije njezina raspona pretraživanja ponovno izračunate, i tada će čitati njihove prethodne vrijednosti. Rezultat konvergira pri sljedećem ponovnom izračunu

To je prihvatljivo jer je to ono što Excel radi. Za radnu knjigu bez uključenog iterativnog izračuna, Excelov vlastiti odgovor za vrijednost još ne izračunatu u trenutnom prolazu jest posljednja izračunata vrijednost, pa motor koji to ponašanje reproducira poklapa referentnu implementaciju umjesto da je aproksimira. Ako trebate istinski konvergirani odgovor nad samoreferentnim modelom, mehanizam za to jest iterativni izračun s izričitom granicom iteracija, pokriven u članku o iterativnom izračunu, i primjenjuje se na stvarne cikluse umjesto na preklapanja skeniranja

Opasnost od regresije skrivena unutar ispravka

Dodavanje LookupScan u TXLSDepRange uvelo je rizik koji nema nikakve veze s pretraživanjima i sve s Pascalom. TXLSDepRange je neupravljani zapis, pa lokalna varijabla tog tipa nije inicijalizirana nulama. Svako mjesto u bazi kôda koje ga gradi ručno, uključujući blokove ovisnosti tablica podataka i nekoliko pomoćnika za testiranje, moralo se stoga ažurirati da izričito postavi novo polje. Preskočite jedno i bilo koji bajt koji je slučajno bio na stogu odlučuje je li ta referenca tretirana kao rub skeniranja, što proizvodi grešku ponovnog izračuna koja se pojavljuje i nestaje s nevezanim promjenama kôda

// Novo Boolean polje u neupravljanom zapisu čini svako ručno
// gradilište latentnom greškom. Dva sigurna idiomatska oblika:
var
  R: TXLSDepRange;
begin
  FillChar(R, SizeOf(R), 0);      // postavi sve na nulu, zatim popuni
  R.Sheet1 := SheetIndex;
  R.Sheet2 := SheetIndex;
  R.Row1 := Row; R.Col1 := Col;
  R.Row2 := Row; R.Col2 := Col;

  // ili postavi svako polje, uključujući novo, na svakom mjestu
  R.LookupScan := False;
end;

Opće pravilo koje je ovo zaradilo: dodavanje polja zapisu koji se gradi na stogu na više od par mjesta promjena je višeg rizika nego što izgleda, i kompajler vam neće pomoći pronaći mjesta. Ako je zapis dostižan iz vrućeg puta, preferirajte pomoćnika koji ga potpuno inicijalizira umjesto vjerovanja da će se svako mjesto poziva ažurirati

Razlikovanje stvarnog ciklusa od preklapanja skeniranja

Ništa u ovoj promjeni ne slabi otkrivanje ciklusa. =B7+1 u B7 i dalje je ciklus, lanac od tri formule koji se zatvara na sebe i dalje je ciklus, i oba se i dalje javljaju kroz rezultat ponovnog izračuna s članovima ciklusa koji zadržavaju svoje prethodne predmemorirane vrijednosti dok sve izvan ciklusa ostaje aktualno. Ono što se promijenilo jest samo to da argument niza pretraživanja više ne proizvodi cikluse koje Excel ne vidi

Ako pregledavate radnu knjigu i želite znati koje je reference motor stvarno razriješio i kojim redoslijedom, evaluacijski pratitelj alat je za to; članak o evaluacijskom pratitelju formula pokriva kako čitati njegov izlaz. HotXLS je izvorna Delphi i C++Builder komponenta proračunskih tablica koja čita i piše XLS, XLSX, ODS i CSV bez instaliranog Excela, i motor ponovnog izračuna isti je u svakom formatu; trenutna pokrivenost funkcija i motora navedena je na stranici proizvoda HotXLS Delphi spreadsheet component