Artikel Teknis

Rekalkulasi Formula Inkremental di HotXLS untuk Delphi

HotXLS, pustaka Excel asli untuk Delphi dan C++Builder, melakukan rekalkulasi formula inkremental melalui TXLSXWorkbook.Recalculate. Panggilan pertama membangun grafik dependensi formula dan mengevaluasi setiap sel formula; setiap panggilan berikutnya hanya mengevaluasi kembali sel yang terpengaruh oleh penulisan nilai sejak lintasan terakhir, dalam urutan topologis, dalam satu sapuan yang biayanya sebanding dengan jumlah sel kotor alih-alih ukuran buku kerja

Satu keputusan desain itu adalah perbedaan antara model keuangan yang merespons asumsi yang diedit dalam milidetik dan model yang macet selama beberapa detik. Jika Anda membuat laporan di mana segelintir sel input memberi makan ribuan formula hilir, sisa artikel ini menjelaskan apa yang dilakukan grafik, fungsi mana yang memilih keluar dari inkrementalitas, dan bagaimana referensi melingkar dilaporkan alih-alih mengulang selamanya

Mengapa mengubah satu sel merangkum rekalkulasi seratus ribu formula?

Mesin formula naif tidak memiliki memori tentang siapa yang bergantung pada siapa, sehingga satu-satunya langkah aman setelah pengeditan apa pun adalah mengevaluasi semuanya kembali. Lebih buruk lagi, strategi rekursif klasik — ketika formula A merujuk ke formula B, evaluasi B di tempat — mengevaluasi kembali sel yang dirujuk tanpa syarat, mengabaikan nilai apa pun yang disimpan di cache. Rantai formula n yang masing-masing merujuk ke formula sebelumnya membutuhkan O(n²) evaluasi per lintasan penuh, dan referensi melingkar membuat rekursi terjun bebas. Setiap pengembang spreadsheet yang telah menghubungkan model bertingkat ke dalam evaluator rekursif telah melihat kedua mode kegagalan tersebut terjadi

Excel sendiri menyelesaikan ini beberapa dekade lalu dengan rantai perhitungannya: pengurutan sel formula dipertahankan sehingga pengeditan menandai sekumpulan kecil sel kotor dan mesin hanya berjalan di sepanjang ekor rantai yang terpengaruh. HotXLS menerapkan ide yang sama sebagai grafik dependensi eksplisit, dibangun sekali dari pohon formula yang dikompilasi dan digunakan kembali di seluruh lintasan rekalkulasi. Intinya bukan kecerdasan; melainkan bahwa biaya rekalkulasi harus melacak ukuran pengeditan Anda, bukan ukuran buku kerja Anda

Bagaimana grafik dependensi mengubah pengeditan menjadi lintasan tunggal

Grafik dependensi HotXLS memberikan satu simpul (node) untuk setiap sel formula, dengan tepi (edges) yang berjalan dari preseden ke dependen. Ketika kode Anda menulis nilai sel, buku kerja mencatat sel tersebut sebagai kotor; ketika Recalculate berjalan, kekotoran merambat di sepanjang tepi ke setiap formula hilir, dan subgrafik kotor dievaluasi tepat sekali dalam urutan topologis menggunakan algoritma Kahn. Karena formula tidak pernah dikunjungi sebelum presedennya, setiap simpul membutuhkan evaluasi tunggal — itulah yang membuat lintasan tersebut bernilai O(dirty)

Urutan topologis juga memperbaiki masalah rekursi pada akarnya. Selama lintasan rekalkulasi, mesin beralih ke mode khusus di mana setiap referensi ke sel formula lain membaca nilai cache sel tersebut secara langsung alih-alih mengevaluasi ulang — pengurutan menjamin bahwa cache sudah segar. Mekanisme yang sama berarti siklus referensi tidak dapat memicu rekursi tanpa batas: tidak ada apa pun di dalam lintasan yang pernah masuk kembali ke evaluator untuk sel tetangga

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;                 // asumsi pertumbuhan
    Model.Cells[2, 2].Formula := 'Inputs!B2*1000';    // formula XLSX tidak memerlukan awalan '='
    Model.Cells[3, 2].Formula := 'B2*(1+Inputs!B2)';
    // ... ribuan baris lagi mengalir dari asumsi yang sama ...

    Book.Recalculate;                 // panggilan pertama: membangun grafik, evaluasi penuh

    Inputs.Cells[2, 2].Value := 0.07; // satu pengeditan menandai satu sel kotor
    Book.Recalculate;                 // panggilan kedua: hanya rantai hilir yang berjalan
  finally
    Book.Free;
  end;
end;

Setiap hasil mendarat di Value cache sel, sehingga setelah Recalculate kembali Anda membaca output dengan cara yang sama seperti Anda membaca sel lainnya. Dalam perulangan pembuatan laporan, polanya persis dengan kode di atas: memuat atau membangun model sekali, lalu bergantian antara menulis beberapa sel input dan memanggil Recalculate, hanya membayar untuk formula yang benar-benar bergantung pada apa yang berubah

Fungsi Excel mana yang memaksa rekalkulasi pada setiap lintasan?

HotXLS memperlakukan NOW, TODAY, RAND, OFFSET, dan INDIRECT sebagai volatil: formula apa pun yang mengandung salah satunya akan dievaluasi ulang pada setiap lintasan Recalculate, apakah ada perubahan di hulu atau tidak. Tiga fungsi pertama volatil karena alasan yang sama dengan di Excel — hasilnya bergantung pada momen evaluasi, bukan pada sel lain. OFFSET dan INDIRECT volatil karena alasan yang lebih halus: sel yang mereka baca dihitung saat runtime, sehingga grafik tidak dapat mengetahui secara statis tepi mana yang harus digambar untuk mereka

Aturan konservatif yang sama berlaku untuk referensi yang tidak dapat ditetapkan oleh pembangun grafik ke dalam satu persegi panjang. Formula yang melalui rentang bernama multi-area, atau yang merujuk ke buku kerja eksternal, juga diturunkan menjadi volatil dan dievaluasi ulang setiap lintasan. Kebijakan ini disengaja: evaluasi ekstra memerlukan sedikit waktu, tetapi tepi dependensi yang hilang berarti nilai usang secara senyap dalam laporan yang dikirimkan, dan itu adalah kegagalan yang jauh lebih buruk. Jika model Anda bergantung pada nama cakupan buku kerja (workbook-scoped names), artikel pendamping tentang nama yang ditentukan dan formula lintas lembar membahas bagaimana nama area tunggal diselesaikan — nama-nama tersebut berpartisipasi dalam grafik secara normal

Panduan praktisnya mengikuti secara langsung. Pertahankan jalur penting (hot paths) dari model besar pada referensi sel dan rentang biasa di mana grafik dapat melakukan tugasnya, dan karantina OFFSET dan INDIRECT ke beberapa tempat yang benar-benar memerlukan pengalamatan dinamis. Model dengan seribu formula volatil menjalankan kembali seribu formula tersebut setiap lintasan tidak peduli seberapa kecil pengeditannya — persis perilaku yang diketahui pengguna Excel dari buku kerja yang "melakukan rekalkulasi pada setiap penekanan tombol"

Bagaimana HotXLS melaporkan referensi melingkar?

TXLSXWorkbook.Recalculate mengembalikan lxOk pada lintasan bersih dan lxErrorRef ketika mendeteksi siklus referensi. Anggota siklus diidentifikasi selama pengurutan topologis — mereka adalah simpul yang tidak pernah dapat dilepaskan oleh algoritma Kahn — dan mereka dilewati alih-alih diputar secara berulang: nilai cache mereka tetap seperti apa adanya, sementara setiap formula di luar siklus tetap dievaluasi secara normal secara berurutan. Tempat pemanggilan Anda mendapatkan kode kesalahan yang pasti alih-alih hang

case Book.Recalculate of
  lxOk:
    SaveReport(Book);
  lxErrorRef:
    // siklus referensi ada; anggota siklus mempertahankan nilai
    // cache sebelumnya dan semua yang ada di luar siklus adalah mutakhir
    LogWarning('Circular reference detected - review model inputs');
end;

Menemukan sel mana yang membentuk siklus adalah tugas debugging, dan formula evaluation tracer adalah alat yang tepat untuk itu: lacak formula yang dicurigai dan rantai referensi yang melipat kembali ke dirinya sendiri menjadi terlihat langkah demi langkah. Siklus dalam model nyata hampir selalu merupakan kesalahan penulisan — baris ringkasan yang secara tidak sengaja dimasukkan dalam rentang SUM-nya sendiri — sehingga kode kesalahan yang keras pada saat rekalkulasi adalah apa yang Anda inginkan

Formula larik, pelacakan kotor, dan kapan grafik dibangun kembali

Formula larik CSE mendapatkan satu simpul untuk seluruh persegi panjang yang ditambatkan, bukan satu simpul per sel. Formula akar dievaluasi sekali per lintasan; matriks yang dihasilkan ditulis langsung ke setiap sel anggota, dan formula yang merujuk ke sel mana pun di dalam rentang yang ditambatkan — tidak hanya jangkar kiri atas — mengambil tepi dependensi dari simpul akar tersebut. Hasil skalar disiarkan ke seluruh persegi panjang seperti yang ditentukan oleh semantik larik warisan Excel

Kait pelacakan kotor menghubungkan penyetel properti biasa, sehingga tidak ada yang berubah dari kode Anda. Menulis Value pada sel memberi tahu buku kerja dan menandai dependen sebagai kotor; menetapkan Formula baru adalah perubahan struktural, sehingga menandai seluruh grafik sebagai usang, dan Recalculate berikutnya membangunnya kembali sebelum mengevaluasi. Menambah, menghapus, atau memindahkan lembar juga membatalkan grafik, karena identitas simpul menyandikan indeks lembar. Ketika tidak ada grafik yang aktif — buku kerja yang tidak pernah Anda panggil Recalculate — kait hanya membebani satu pemeriksaan nil per penetapan, sehingga beban kerja baca-tulis biasa tidak terpengaruh

Satu batasan yang patut dinyatakan secara jujur: grafik melacak dependensi antar sel, sehingga fungsi yang ditentukan pengguna yang didaftarkan melalui OnUserFunction dievaluasi ulang ketika sel yang memberi makan argumennya berubah, seperti formula lainnya. Jika Anda memperluas mesin dengan cara itu, artikel tentang fungsi kustom di mesin formula HotXLS membahas kontrak panggilan balik dan bagaimana nilai argumen tiba

Rekalkulasi inkremental adalah bagian dari mesin XLSX standar dalam HotXLS Delphi Excel Component, bersama dengan kalkulator formula, nama yang ditentukan, dan alur impor/ekspor yang dipercepatnya. Jika aplikasi Delphi atau C++Builder Anda mempertahankan model hidup — lembar harga, buku kerja konsolidasi, cascades laporan — Recalculate adalah perbedaan antara menghitung ulang buku kerja dan menghitung ulang pengeditan