Τεχνικό Άρθρο

Πίνακας επιλογών AGGREGATE και διαρροή πύλης στο HotXLS

Το HotXLS, το εγγενές component λογιστικών φύλλων Excel για Delphi και C++Builder, κυκλοφόρησε δύο συναφή fixes AGGREGATE τον Σεπτέμβριο του 2026. Η έκδοση 2.382.0 διόρθωσε το όρισμα επιλογών ώστε οι κωδικοί 1/3/5/7 να αγνοούν κρυφές σειρές, οι 2/3/6/7 να αγνοούν σφάλματα, και οι 0 έως 3 να αγνοούν εμφωλευμένα κελιά SUBTOTAL και AGGREGATE, ακριβώς όπως τεκμηριώνει η Microsoft. Η έκδοση 2.382.3 μετά σταμάτησε εκείνες τις σημαίες επιλογής από το να διαρρέουν στην αξιολόγηση των ακριβώς κελιών που αναφέρεται η συνάρτηση. Το πρώτο ελάττωμα είναι αμήχανο με τον τρόπο που είναι πάντα τα bugs αντιγραφής πινάκων: οι θέσεις bits ήταν αντεστραμμένες, οπότε κάθε τύπος που χρησιμοποιούσε μη μηδενικό κωδικό επιλογών έπαιρνε μια πολιτική που δεν ζήτησε ο συγγραφέας του. Το δεύτερο είναι πιο ενδιαφέρον, επειδή είναι σχήμα που θα συναντήσεις σε οποιονδήποτε evaluator που χρησιμοποιεί παροδικό πεδίο για να περάσει πλαίσιο σε αναδρομικό περπάτημα. Μια εξωτερική συσσωμάτωση οπλίζει μια σημαία, περπατάει ένα εύρος, και τραβάει ένα κελί του οποίου ο τύπος δεν έχει υπολογιστεί ακόμα. Εκείνος ο τύπος τρέχει πάνω στην ίδια αριθμομηχανή, βλέπει την ίδια οπλισμένη σημαία, και συσσωματώνει σιωπηλά τις λάθος σειρές, παράγοντας αριθμό που απέχει κατά ποσό που κανείς δεν μπορεί να εξηγήσει από το κείμενο του τύπου μόνο

Τι επιλέγουν πραγματικά οι επιλογές AGGREGATE 0 έως 7;

Το όρισμα επιλογών του AGGREGATE είναι matrix τριών bits, και τα τρία bits είναι ανεξάρτητα. Το bit 0 (τιμή 1) σημαίνει αγνόησε κρυφές σειρές, το bit 1 (τιμή 2) σημαίνει αγνόησε τιμές σφαλμάτων, και το bit 2 (τιμή 4) σημαίνει σταμάτα να αγνοείς εμφωλευμένα κελιά SUBTOTAL και AGGREGATE, επειδή το να τα παραλείπεις είναι η προεπιλογή για τους χαμηλούς κωδικούς. Δύο πράγματα εδώ είναι εύκολο να τα πάρεις ανάποδα. Το bit κρυφών σειρών είναι το χαμηλό bit, όχι το μεσαίο, οπότε το AGGREGATE(9,1,...) είναι η μορφή φιλτραρισμένου συνόλου και το AGGREGATE(9,2,...) είναι η ανεκτική στα σφάλματα. Και η πολιτική εμφωλευμένων aggregates είναι αντεστραμμένη σε σχέση με τις άλλες δύο: μόνο οι κωδικοί 4 έως 7 μεταχειρίζονται κελί του οποίου ο ίδιος τύπος είναι SUBTOTAL ή AGGREGATE ως συνηθισμένη τιμή. Το ECMA-376 Part 1 §18.17.7 ορίζει το SUBTOTAL με τον ίδιο διαχωρισμό συμπερίληψης-ή-εξαίρεσης κρυφών σειρών στους κωδικούς 1-11 και 101-111, και το AGGREGATE, αποθηκευμένο σε αρχεία OOXML υπό το πρόθεμα _xlfn., γενικεύει εκείνον τον διαχωρισμό στο όρισμα επιλογών, οπότε ο πίνακας που δημοσιεύει η Microsoft για τη συνάρτηση AGGREGATE είναι η σύμβαση που πρέπει να πληροί μια μηχανή και όχι ευκολία

ΕπιλογήΚρυφές σειρέςΤιμές σφάλματοςΕμφωλευμένα SUBTOTAL / AGGREGATE
0περιλαμβάνονταιδιαδίδονταιαγνοούνται
1αγνοούνταιδιαδίδονταιαγνοούνται
2περιλαμβάνονταιαγνοούνταιαγνοούνται
3αγνοούνταιαγνοούνταιαγνοούνται
4περιλαμβάνονταιδιαδίδονταιπεριλαμβάνονται
5αγνοούνταιδιαδίδονταιπεριλαμβάνονται
6περιλαμβάνονταιαγνοούνταιπεριλαμβάνονται
7αγνοούνταιαγνοούνταιπεριλαμβάνονται

Γιατί το HotXLS είχε τις επιλογές AGGREGATE ανάποδα;

Επειδή το αρχικό TXLSCalculator.CalcAggregateFunc γράφτηκε από μια παράφραση του πίνακα και όχι από τον πίνακα. Υπολόγιζε ignoreErrors := (optCode >= 4) and (optCode <= 7) και οπλίζε την πύλη κρυφών σειρών για τους κωδικούς 2, 3, 6 και 7, ενώ η πολιτική εμφωλευμένων aggregates δεν ήταν καθόλου υλοποιημένη. Το προγενέστερο άρθρο για τις κρυφές σειρές SUBTOTAL και AGGREGATE απαριθμούσε εκείνο το κενό ως ανοιχτό όριο και περιέγραφε την παλιά αντιστοίχιση όπως κυκλοφορούσε τότε· η περιγραφή ήταν ακριβής για τον κώδικα και λάθος για το Excel, και κανείς δεν το πρόσεξε για πολύ καιρό επειδή οι δύο πολιτικές που συνδυάζουν οι περισσότεροι, κρυφές συν σφάλματα, προσγειώνονται στους κωδικούς 3 και 7 και στις δύο πίνακες. Μόνο ένας κωδικός ενός bit αποκάλυπτε την αντιστροφή: το AGGREGATE(9,1,A1:A4) επέστρεφε το άφιλτρο άθροισμα, και το AGGREGATE(9,2,...) παραλείπε κρυφές σειρές ενώ ακόμα διέδιδε #DIV/0!. Το ελάττωμα βγήκε στην επιφάνεια από μια στατική αναθεώρηση του lxCalc.pas, καταγεγραμμένο ως HXLS-008 στο μητρώο γνωστών θεμάτων του έργου, και όχι από αρχείο πελάτη, που λέει κάτι για το πόσο σπάνια εμφανίζονται οι κωδικοί ενός bit σε workbooks παραγωγής. Η έκδοση 2.382.0 ξαναέγραψε την αποκωδικοποίηση ως τρία tests συμμετοχής συνόλου και πρόσθεσε δεύτερη πύλη για την εμφωλευμένη πολιτική, συνδεδεμένη μέσω νέου callback TXLSIsSubtotalCell που παρέχει το workbook δίπλα στο TXLSIsRowHidden

Η αποκωδικοποίηση επιλογών AGGREGATE του HotXLS πριν και μετά το v2.382.0: το αρχικό CalcAggregateFunc οπλίζε την πύλη κρυφών σειρών για κωδικούς 2, 3, 6, 7 και αγνοούσε σφάλματα από 4 και πάνω χωρίς εμφωλευμένη πολιτική, ενώ η διορθωμένη αποκωδικοποίηση ελέγχει κρυφές σειρές σε 1, 3, 5, 7, σφάλματα σε 2, 3, 6, 7 και εμφωλευμένες παραλείψεις σε 0 έως 3
Μόνο κωδικοί ενός bit αποκάλυπταν την αντιστροφή επειδή ο δημοφιλής συνδυασμός κρυφών-συν-σφαλμάτων προσγειώνεται στους κωδικούς 3 και 7 και στους δύο πίνακες, και κωδικοί εκτός 0 έως 7 επιστρέφουν πλέον lxErrorValue ακριβώς όπως τους απορρίπτει το Excel
// TXLSCalculator.CalcAggregateFunc, μορφή v2.382.3
if (optCode < 0) or (optCode > 7) then
begin
  Result := lxErrorValue;            // Το Excel απορρίπτει κωδικούς εκτός 0..7
  Exit;
end;
ignoreErrors := optCode in [2, 3, 6, 7];
prevIgnoreHidden := FIgnoreHiddenRows;
prevIgnoreSubtotal := FIgnoreSubtotalCells;
FIgnoreHiddenRows := (optCode in [1, 3, 5, 7]) and Assigned(FIsRowHidden);
FIgnoreSubtotalCells := (optCode in [0, 1, 2, 3]) and Assigned(FIsSubtotalCell);
try
  // ... αντιστοίχιση function_num στο εσωτερικό iftab, περπάτημα ref1..refN ...
finally
  FIgnoreHiddenRows := prevIgnoreHidden;
  FIgnoreSubtotalCells := prevIgnoreSubtotal;
end;

Πρόσεξε ότι οι δύο σημαίες ανατίθενται άνευ όρων και όχι μόνο θέτονται όταν το ζητά η επιλογή. Η έκδοση v2.382.0 ακόμα χρησιμοποιούσε if ... then FIgnoreHiddenRows := True, που σήμαινε ότι ένα AGGREGATE με κωδικό 4 εμφωλευμένο μέσα σε SUBTOTAL(109, ...) κληρονομούσε την εξωτερική πύλη κρυφών σειρών αντί να την καθαρίσει. Η ανάθεση της αποκωδικοποιημένης τιμής στην είσοδο και η επαναφορά της προηγούμενης τιμής στο block finally κάνει κάθε κλήση AGGREGATE να κατέχει τη δική της πολιτική για τη διάρκεια του περπατήματός της και τίποτα παραπάνω. Η έκδοση 2.382.0 έκανε επίσης την τυπική μορφή πίνακα ειλικρινή: όταν ένα όρισμα αξιολογείται σε Variant array ενός ή δύο διαστάσεων, το CalcAggregateFunc πλέον περπατάει κάθε στοιχείο και εφαρμόζει την πολιτική σφαλμάτων ανά στοιχείο, εκεί όπου ο παλιός κώδικας έλεγχε μόνο για NaN double και αλλιώς παρεδίδε όλο τον πίνακα στο ExcelSum

Γιατί ένα εξωτερικό AGGREGATE διαρρέει στους τύπους που αναφέρεται;

Επειδή τα FIgnoreHiddenRows και FIgnoreSubtotalCells είναι πεδία της αριθμομηχανής, και η αριθμομηχανή μοιράζεται από κάθε τύπο που αξιολογείται σε μία επανυπολογισμό. Οι πύλες σχεδιάστηκαν ως scratch πεδία ακριβώς ώστε έξι βρόχοι περπατήματος κελιών να τα συμβουλεύονται χωρίς να περνάει παράμετρος μέσα από κάθε υπογραφή, και εκείνος ο σχεδιασμός είναι υγιής όσο οτιδήποτε τρέχει όσο μια πύλη είναι οπλισμένη ανήκει στη συσσωμάτωση που την οπλίσε. Η υπόθεση σπάει σε ένα συγκεκριμένο σημείο: το FGetValue. Όταν ένας walker ζητά από το workbook τιμή κελιού και εκείνο το κελί κρατά τύπο χωρίς cached αποτέλεσμα, το workbook μεταγλωττίζει τον τύπο και τον αξιολογεί επιτόπου, πάνω στην ίδια TXLSCalculator, με τις εξωτερικές πύλες ακόμα ορισμένες. Το regression fixture στο HotXLS.WorkbookApiTests.pas δείχνει την αποτυχία με τέσσερα κελιά. Το A1 κρατά 10, το A2 κρατά 20 σε κρυφή σειρά, το A3 κρατά =1/0, και το A4 κρατά =SUBTOTAL(9,A1:A2), του οποίου η σωστή τιμή είναι 30. Τώρα αξιολόγησε =AGGREGATE(9,7,A1:A4): αγνόησε κρυφές σειρές, αγνόησε σφάλματα, μέτρησε το εμφωλευμένο subtotal ως τιμή. Το Excel επιστρέφει 10 + 30 = 40. Με το A4 χωρίς cache, η μηχανή προ-2.382.3 οπλίζε την πύλη κρυφών σειρών, περπατούσε μέχρι το A4, πυροδοτούσε την αξιολόγησή του, και το CalcSubtotalFunc για κωδικό 9 κληρονομούσε την οπλισμένη πύλη, επειδή θέτει μόνο τη σημαία για κωδικούς 101 έως 111 και δεν την καθαρίζει ποτέ. Το A4 αξιολογούνταν σε 10 αντί για 30, και το εξωτερικό σύνολο γύριζε 20. Τίποτα σε κάποιον από τους δύο τύπους δεν μνημονεύει κρυφές σειρές στη διαδρομή που παρήγε τον λάθος αριθμό

Πώς ένα εξωτερικό AGGREGATE HotXLS διαρρέει στους προηγούμενους τύπους του: με το FIgnoreHiddenRows οπλισμένο για κωδικό 7, το περπάτημα φτάνει στο χωρίς cache A4 που κρατά SUBTOTAL 9 πάνω στο A1:A2, το FGetValue το αξιολογεί στην ίδια αριθμομηχανή, το CalcSubtotalFunc κληρονομεί την πύλη και επιστρέφει 10 αντί για 30, οπότε το σύνολο αναφέρει 20 εκεί όπου το Excel επιστρέφει 40
Η εμφωλευμένη πύλη διέρρεε και προς την άλλη κατεύθυνση, και το CalcSubtotalFunc μηδένιζε το FIgnoreSubtotalCells στην έξοδο αντί να το επαναφέρει, αφοπλίζοντας την εξωτερική πολιτική για κάθε κελί μετά από ένα χωρίς cache subtotal που φτάστηκε στη μέση του περπατήματος

Η πύλη εμφωλευμένων aggregates διέρρεε με τον ίδιο τρόπο προς την άλλη κατεύθυνση. Με κωδικούς 0 έως 3, το FIgnoreSubtotalCells είναι οπλισμένο, και ο γενικός walker εύρους στο GetValueItemRange το τηρεί, οπότε προηγούμενος τύπος του οποίου ο τύπος είναι =SUM(B1:B3) θα πετούσε σιωπηλά το B2 αν το B2 τύχαινε να περιέχει SUBTOTAL. Χειρότερα, το CalcSubtotalFunc μηδένιζε το FIgnoreSubtotalCells σε False στην έξοδο αντί να επαναφέρει την προηγούμενη τιμή, οπότε ένας χωρίς cache πρόγονος SUBTOTAL που φτάνονταν στη μέση του περπατήματος αφοπλίζε την εξωτερική πύλη για κάθε κελί μετά από αυτόν. Το μητρώο γνωστών θεμάτων του έργου το καταχωρεί ως HXLS-008 ως διαρροή εμφωλευμένης κατάστασης επιλογής, και αυτό είναι το σωστό όνομα για την κατηγορία bug: μια καθολική παροδική σημαία που είναι σωστή για το πλαίσιο που την έθεσε και λάθος για κάθε πλαίσιο που την κληρονομεί

Πώς το AggregateGetCellValue και το AggregateGetItemValue μονώνουν το περπάτημα

Το fix στο v2.382.3 βάζει όριο γύρω από κάθε σημείο όπου το AGGREGATE διαβάζει τιμή που δεν υπολόγισε ο ίδιος. Το TXLSCalculator.AggregateGetCellValue τυλίγει την ακατέργαστη κλήση FGetValue: σώζει και τις δύο σημαίες, τις καθαρίζει, εκτελεί το τράβηγμα, και τις επαναφέρει σε block finally. Η εξωτερική συσσωμάτωση εξακολουθεί να εφαρμόζει τη δική της πολιτική στο κελί που μόλις τράβηξε, επειδή τα tests κρυφών σειρών και εμφωλευμένων κελιών γίνονται στον walker γύρω από το τράβηγμα, αλλά ο ίδιος ο πρόγονος τύπος τρέχει χωρίς καθόλου πολιτική, που είναι ακριβώς ό,τι κάνει το Excel

Η μόνωση v2.382.3 του HotXLS: το AggregateGetCellValue σώζει και τις δύο σημαίες πύλης, τις καθαρίζει, τραβάει μέσω FGetValue και τις επαναφέρει σε block finally, ώστε προηγούμενος τύπος να αξιολογείται χωρίς καμία πολιτική ενώ ο εξωτερικός walker εξακολουθεί να εφαρμόζει tests κρυφών σειρών και εμφωλευμένων κελιών γύρω από το τράβηγμα
Το AggregateGetItemValue κάνει το ίδιο για υπολογιζόμενα ορίσματα πινάκων και αντιστοιχίζει σφάλματα τραβήγματος σε VarAsError, ενώ κωδικός ορίου πόρων σκόπιμα δεν μεταχειρίζεται ποτέ ως αγνοήσιμο σφάλμα κάτω από τις επιλογές αγνόησης σφαλμάτων
function TXLSCalculator.AggregateGetCellValue(SheetIndex, Row, Col: Integer;
  var Value: Variant; var OutOfRange: Boolean): Integer;
var
  Hidden, Nested: Boolean;
begin
  Hidden := FIgnoreHiddenRows;
  Nested := FIgnoreSubtotalCells;
  FIgnoreHiddenRows := False;        // ένας πρόγονος τύπος κατέχει τη δική του πολιτική
  FIgnoreSubtotalCells := False;
  try
    Result := FGetValue(SheetIndex, Row, Col, Value, OutOfRange);
  finally
    FIgnoreHiddenRows := Hidden;
    FIgnoreSubtotalCells := Nested;
  end;
end;

Το AggregateGetItemValue κάνει το ίδιο για ορίσματα εκτός εύρους, και πρέπει να κάνει περισσότερα από το να καθαρίσει σημαίες, επειδή ένα όρισμα όπως A1:A4/(B1:B4-20) είναι υπολογιζόμενος πίνακας του οποίου το σχήμα στοιχείων πρέπει να επιβιώσει. Ο wrapper υλοποιεί σκέτο εύρος σε δισδιάστατο Variant array μέσω AggregateGetCellValue, αντιστοιχίζοντας κελί που επέστρεψε κωδικό σφάλματος σε VarAsError ώστε η πολιτική σφαλμάτων να εφαρμόζεται ακόμα ανά στοιχείο, και αναδύεται μέσα από τους κόμβους δυαδικών και μονάικών operators (SA_ADD, SA_DIV, SA_UNARMINUS, και τους υπόλοιπους) με ApplyArrayBinaryOp και ApplyArrayUnaryOp· οτιδήποτε άλλο πέφτει στο συνηθισμένο GetValueItem. Δύο guards κάθονται μπροστά από την υλοποίηση: εύρος μεγαλύτερο από EffectiveFormulaArrayMemoryLimit επιστρέφει lxErrorResourceLimit, και εύρος πολλαπλών φύλλων ή αντεστραμμένο επιστρέφει #VALUE!. Κωδικός ορίου πόρων σκόπιμα δεν μεταχειρίζεται ως αγνοήσιμο σφάλμα κελιού ούτε κάτω από επιλογές 2/3/6/7, αφού μια μηχανή που θα καταπίνε το δικό της σήμα out-of-memory επειδή ο χρήστης ζήτησε να παραλείπεται το #N/A θα έλεγε ψέματα. Και οι τρεις walkers AGGREGATE, το AggregateCollectRange για την οικογένεια SUM, το AggregateReduceVariance για STDEV, VAR και PRODUCT, και το AggregateReduceWithK για MEDIAN και τις μορφές ποσοστημορίων, άλλαξαν από FGetValue και GetValueItem στους δύο wrappers, και ο καθένας απέκτησε το test εμφωλευμένων κελιών μέσω FIsSubtotalCell

Ποιο σφάλμα επιστρέφει το AGGREGATE όταν δεν αγνοεί σφάλματα;

Το πρωτότυπο, από το v2.382.3. Η έκδοση 2.382.0 ανίχνευε σωστά κελιά σφαλμάτων αλλά κατέρρεε καθένα από αυτά σε lxErrorValue, οπότε το AGGREGATE(9,4,A1:A3) πάνω σε κελί #DIV/0! επέστρεφε #VALUE!, εκεί όπου το Excel διαδίδει το πρώτο σφάλμα που συναντά αμετάβλητο. Ο αντικαταστάτης helper AggregateErrorCode αντιστοιχίζει Variant στον ταιριάζοντα κωδικό lxError*, είτε το Variant είναι γνήσιο varError είτε ένα από τα επτά strings σφαλμάτων, και το AggregateValueIsError είναι πλέον απλώς test για μη μηδενικό αποτέλεσμα. Κάθε walker καταγράφει τον πρώτο κωδικό σφάλματος που βλέπει και επιστρέφει εκείνον τον κωδικό, που σημαίνει επίσης ότι κελί του οποίου ο τύπος δεν υπολογίστηκε ποτέ, και του οποίου το σφάλμα άρα φτάνει ως κωδικός επιστροφής από το FGetValue και όχι ως cached Variant, διαδίδεται με τον ίδιο τρόπο με ένα cached. Δύο συναρτήσεις μέτρησης παίρνουν ειδική μεταχείριση μέσα στο AggregateCollectRange, και η μεταχείριση ταιριάζει το SUBTOTAL και όχι το SUM. Για εσωτερική συνάρτηση 0, COUNT, κελί σφάλματος δεν μετράται ποτέ και δεν διαδίδεται ποτέ ανεξαρτήτως κωδικού επιλογών, επειδή το COUNT μετρά μόνο αριθμούς. Για εσωτερική συνάρτηση 169, COUNTA, κελί σφάλματος είναι μη κενή τιμή και μετρά ως 1 εκτός αν ο κωδικός επιλογών αγνοεί σφάλματα, οπότε παραλείπεται. Εκείνη η ασυμμετρία είναι πώς το Excel μεταχειρίζεται τα COUNT και COUNTA και εκτός AGGREGATE, και είναι το είδος λεπτομέρειας που ένας γενικός κανόνας «αν σφάλμα τότε διάδοση» παίρνει σιωπηλά λάθος

Τι επαληθεύει η matrix regression των οκτώ επιλογών

Το fixture που περιγράφηκε παραπάνω εξασκείται ως πλήρης matrix στο AggregateFunc_OptionMatrixCoversHiddenErrorsAndNestedAggregates: για κάθε κωδικό επιλογών από 0 έως 7 αξιολογεί και τη μορφή SUM και τη μορφή MEDIAN πάνω στο A1:A4 και ελέγχει το αποτέλεσμα απέναντι σε χειροκίνητα παραγόμενη προσδοκία. Οι κωδικοί 0, 1, 4 και 5 πρέπει να διαδώσουν το #DIV/0! από το A3, αφού κανένας δεν αγνοεί σφάλματα. Ο κωδικός 2 δίνει SUM 30 και MEDIAN 15, από 10 και 20 με το εμφωλευμένο A4 παραλειμμένο. Ο κωδικός 3 δίνει 10 και 10. Ο κωδικός 6 δίνει 60 και 20, επειδή το 30 στο A4 μετράει πλέον. Ο κωδικός 7 δίνει 40 και 20, που είναι η περίπτωση που επέστρεφε 20 πριν το fix της διαρροής. Το φαρδύτερο πέρασμα αποδοχής που καταγράφεται στο μητρώο γνωστών θεμάτων καλύπτει και τα δεκαεννιά function numbers απέναντι και στους οκτώ κωδικούς, με κάθε πρόγονο και cached και χωρίς cache, για 304 σενάρια σε Win32 και Win64

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Data');
    Sheet.Cells[1, 1].Value := 10;
    Sheet.Cells[2, 1].Value := 20;
    Sheet.Cells[3, 1].Formula := '=1/0';
    Sheet.Cells[4, 1].Formula := '=SUBTOTAL(9,A1:A2)';   // subtotal ομάδας = 30
    Sheet.RowHidden[2] := True;

    Sheet.Cells[6, 1].Formula := '=AGGREGATE(9,1,A1:A4)'; // #DIV/0!  κρυφές παραλείπονται, το σφάλμα διαδίδεται
    Sheet.Cells[7, 1].Formula := '=AGGREGATE(9,3,A1:A4)'; // 10       κρυφές + σφάλμα + εμφωλευμένα παραλείπονται
    Sheet.Cells[8, 1].Formula := '=AGGREGATE(9,6,A1:A4)'; // 60       μόνο σφάλματα παραλείπονται
    Sheet.Cells[9, 1].Formula := '=AGGREGATE(9,7,A1:A4)'; // 40       ήταν 20 πριν το v2.382.3
    Book.Recalculate;
    Book.SaveAs('aggregate-options.xlsx');
  finally
    Book.Free;
  end;
end;

Πού βρίσκεται ακόμα το όριο

Τρία όρια αξίζει να ξέρεις πριν χτίσεις πάνω σε αυτό. Πρώτον, το κατηγόρημα εμφωλευμένων aggregates είναι κειμενικό. Τα TXLSXWorkbook.GetCalcIsSubtotalCell και το δίδυμό του classic-engine απαντούν True όταν ο τύπος κελιού ξεκινά SUBTOTAL(, AGGREGATE(, ή _xlfn.AGGREGATE(, με ή χωρίς το αρχικό equals sign, οπότε τύπος όπως =IF(C1,SUBTOTAL(9,B1:B9),0) ή =SUBTOTAL(9,B1:B9)*2 δεν αναγνωρίζεται ως εμφωλευμένος και θα μετρηθεί διπλά από τους κωδικούς 0 έως 3 εκεί όπου το Excel θα τον παρέλειπε· ένας generator που εκπέμπει υπολογιζόμενα subtotals πρέπει να κρατά την κλήση συσσωμάτωσης στην κεφαλή του τύπου. Δεύτερον, η μόνωση ζει στους τρεις walkers AGGREGATE. Το CalcSubtotalFunc εξακολουθεί να περπατάει μέσω GetValueItemRange, CollectRangeValues, και SubtotalReduceVariance, που καλούν FGetValue ευθέως, οπότε SUBTOTAL(109, ...) του οποίου το εύρος περιέχει χωρίς cache πρόγονο τύπο μπορεί ακόμα να περάσει την πύλη κρυφών σειρών του μέσα σε εκείνον τον πρόγονο. Ένα πλήρες Recalculate αξιολογεί προγόνους πριν από εξαρτήσεις, οπότε παίρνεται η cached διαδρομή και η πύλη δεν κληρονομείται ποτέ· η έκθεση περιορίζεται σε ad hoc αξιολόγηση μέσω Calculate και σε workbooks φορτωμένα χωρίς cached τιμές, και αν στηρίζεσαι στον incremental επανυπολογισμό πάνω στον γράφο εξαρτήσεων για να κρατάς μεγάλα μοντέλα ανταποκρίτικά, η ίδια εγγύηση σειράς είναι που κρατά αυτή τη διαρροή αδρανή. Τρίτον, και οι δύο πύλες εξαρτώνται από Assigned(FIsRowHidden) και Assigned(FIsSubtotalCell). Και τα δύο facades workbook συνδέουν τα callbacks στους constructors τους, αλλά κώδικας που χτίζει TXLSCalculator με το χέρι με μόνο τα δύο πρωτότυπα ορίσματα παίρνει τη legacy συμπεριφορά συμπερίληψης-όλων για κάθε κωδικό επιλογών, σιωπηλά. Όταν ένα σύνολο φαίνεται λάθος και το κείμενο του τύπου φαίνεται σωστό, το ιχνηλάτηση της αξιολόγησης βήμα προς βήμα είναι ο γρηγορότερος τρόπος να δεις αν ένας πρόγονος αξιολογήθηκε κάτω από κληρονομημένη πύλη ή αν ένα callback απλώς δεν συνδέθηκε ποτέ

Η μηχανή υπολογισμών που περιγράφεται εδώ, ο decoder επιλογών, τα μονωμένα wrappers τραβήγματος, και η matrix regression που τα καρφώνει όλα κυκλοφορούν ως πηγή με το HotXLS Delphi spreadsheet component, που διαβάζει, γράφει, και επανυπολογίζει workbooks XLS, XLSX και ODS σε Delphi και C++Builder χωρίς εγκατάσταση Excel