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
Š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
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
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