Tehnični članak

LAMBDA in LET v Delphiju: zaprtja formul HotXLS

HotXLS ovrednoti Excel LAMBDA kot pravo funkcijsko vrednost prvega razreda. Definirano ime, katerega besedilo RefersTo je LAMBDA, lahko pokličete po imenu kot =MyFunc(5), zaprtje, vezano znotraj LET, pa lahko pokličete kot =LET(f, LAMBDA(x, x*2), f(21)), leksikalno okolje, zajeto v trenutku definicije, pa potuje z zaprtjem. Besedilo formule se povsem nespremenjeno vrne v delovni zvezek

To je funkcija, ki formulski pogon loči od razčlenjevalnika formul. Vse pred LAMBDA je bilo mogoče ovrednotiti s hojo po drevesu vrednosti. LAMBDA zahteva sklad obsegov, ko pa imate sklad obsegov, začne v vaši aplikaciji Delphi delovati cel razred uporabniško napisane logike preglednic, ne le v Excelu

Zakaj se večina pogonov, ki niso Excel, ustavi pri ključni besedi LAMBDA?

Ker ima klasičen ovrednotevalnik preglednic natanko eno vrsto vrednosti: število, niz, logično vrednost, napako ali referenco na celice, ki hranijo te vrednosti. Ni kam postaviti funkcije. Ko je Excel 365 uvedel LAMBDA, je dodal vrsto vrednosti, ki nosi imena parametrov, izraz telesa in vezave, vidne tam, kjer je bila napisana. Pogon brez te vrste lahko razčleni LAMBDA(x, x*2) in shrani besedilo, toda v trenutku, ko jo celica poskusi poklicati, ni ničesar za klicati

HotXLS manjkajoči del vgradi kot vrednost zaprtja plus izvajalni sklad obsegov. Klic zaprtja potisne njegovo zajeto okolje, nato potisne vrednosti argumentov pod imeni parametrov, ovrednoti telo in sklad skrajša nazaj na oznako. Ta vrstni red je pomemben, naslednji razdelek pa pojasni zakaj

Trije načini, kako se LAMBDA pokliče

HotXLS razreši klic neznanega imena funkcije po treh poteh, preizkušenih po vrstnem redu, poznavanje tega, katera se sproži, pa pojasni večino presenečenj. Prvič, ime, vezano v trenutnem obsegu LET ali LAMBDA: če je f lokalna vezava, ki hrani zaprtje, ga f(21) uporabi. Drugič, definirano ime delovnega zvezka, katerega besedilo formule se začne z LAMBDA: MyFunc(5) prevede telo tega imena in ga uporabi. Tretjič, klasičen upravljalnik uporabniških funkcij, nespremenjen, za vse, kar prvi dve poti ne zahtevata

Lokalna vezava, ki hrani kaj drugega kot zaprtje, ni klicljiva. Vežite f na število 3 in nato zapišite f(21) in dobili boste napako vrednosti, ne poskusa množenja. To je strožje, kot bi bil dinamičen jezik, in namerno tako: tipkarska napaka, ki spremeni klic funkcije v naključno referenco, je tiho napačen odgovor, kar je najslabši možni izid, ki ga lahko ustvari pogon preglednic

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Model');

    // Ponovno uporabna imenovana funkcija, obseg delovnega zvezka
    Book.DefinedNames.Add('NetOf', 'LAMBDA(amount, rate, amount*(1-rate))');

    Sheet.Cells[2, 2].Formula := 'NetOf(1250, 0.19)';

    // Zaprtje, vezano in uporabljeno znotraj ene formule
    Sheet.Cells[3, 2].Formula := 'LET(double, LAMBDA(x, x*2), double(21))';

    // Vgnezden LET: vsaka vezava je vidna vsem, ki sledijo
    Sheet.Cells[4, 2].Formula :=
      'LET(base, 100, bump, LAMBDA(v, v+base), LET(step, bump(5), step*2))';

    Book.Recalculate;
    Book.SaveAs('lambda-model.xlsx');
  finally
    Book.Free;
  end;
end;

Kako se razreši senčenje, ko se imena prekrivajo?

Zmagajo parametri. Ko HotXLS uporabi zaprtje, najprej potisne zajeto leksikalno okolje, nato pa vezave argumentov, zato parameter, imenovan rate, zasenči zunanjo vezavo, imenovano rate, in tudi enako zapisano referenco na stolpec v okoliški formuli. Ta vrstni red je tisto, kar naredi imenovano funkcijo varno za ponovno uporabo: klicatelj ne more nehote spremeniti pomena telesa s podobno imenovano vezavo v obsegu

Aritnost se preveri, preden se karkoli ovrednoti. Klic, katerega število argumentov se ne ujema s številom parametrov zaprtja, takoj vrne napako vrednosti, namesto da bi ovrednotil nekaj argumentov in nato odpovedal, kar ohranja ovrednotenje brez stranskih učinkov resnično prosto delnega dela. Sklad obsegov se v bloku finally skrajša nazaj na svojo vstopno oznako, tako da napaka znotraj telesa ne more pustiti zastarelih vezav vidnih naslednji formuli

var
  Book: TXLSXWorkbook;
  Name: TXLSXDefinedName;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open('customer-model.xlsx') = 1 then
    begin
      // Preverite, kaj je uporabnik napisal, preden zaupate ponovnemu izračunu
      Name := Book.DefinedNames.FindByName('NetOf');
      if (Name <> nil) and
         (UpperCase(Copy(Name.Formula, 1, 6)) = 'LAMBDA') then
        Log('Named lambda found: ' + Name.Formula);

      Book.Recalculate;
      Log(VarToStr(Book.Sheets[1].Cells[2, 2].Value));
    end;
  finally
    Book.Free;
  end;
end;

LET ni več delen

Prejšnje izdaje HotXLS so LET izvajale le do te mere, da so obravnavale pogost primer ene same vezave. Trenutna izvedba je popolna: vsaka vezava je vidna vsem poznejšim vezavam in izrazu telesa, vgnezden LET pa se sestavlja normalno, tako da se LET(a, 1, b, a+1, LET(c, b*2, c)) ovrednoti tako, kot ga ovrednoti Excel

Ta popolnost je pomembnejša, kot se sliši. LET je način, s katerim se uporabniki izognejo ponovnemu izračunu istega podizraza petkrat v eni formuli, zato resnični delovni zvezki uporabljajo LET natanko v tistih globoko vgnezdenih oblikah, pri katerih delna izvedba zgreši. Če ste prej vrzeli obhajali z razširitvijo vezav LET pred ovrednotenjem, lahko ta obvod odpade

Vejica ali podpičje: zdaj oboje

Besedilo formul v HotXLS zdaj poleg klasičnega podpičja sprejema tudi vejico kot ločilo argumentov. To ni nastavitev področja; je pravilo sprejemanja v razčlenjevalniku. Pomembno je, ker formule prihajajo od koder ne nadzorujete: prilepljene iz podpornega zahtevka, prekopirane iz dokumentacije, ustvarjene s skripto, ki je izdala Excelovo kanonično sintakso, uvožene iz CSV z nizi formul

Praktični učinek je, da se prevedeta oba, SUM(A1,A2) in SUM(A1;A2). Povratno pretvarjanje ohrani, karkoli je uporabil vir, tako da je delovni zvezek, ki ste ga naložili, zapisan nazaj z izvirnimi ločili in ne normaliziran mimo uporabnikovega vedenja

Kaj se povratno pretvori, in kaj preveriti

Besedilo formule je shranjeno dobesedno, zato LAMBDA v definiranem imenu preživi cikel nalaganja in shranjevanja nedotaknjena in se v Excelu odpre kot ista funkcija. Gola LAMBDA, shranjena kot rezultat celice, torej formula, ki se ovrednoti v zaprtje in ne v vrednost, ohrani obstoječe vedenje preskoči-brez-vrednosti: besedilo je ohranjeno, zanjo pa se ne izmisli nobenega predpomnjenega številskega rezultata. To je pošten izid, saj ni skalarja za predpomnjenje

Vredno je usvojiti dve navadi. Imenovanim lambdam dajte obseg delovnega zvezka, razen če obstaja razlog, da tega ne storite, ker funkcija z obsegom lista, ki izgine, ko je list kopiran, ustvari napako imena na mestu, oddaljenem od vzroka; pravila o obsegu so obravnavana v definiranih imenih in formulah med listi. In ko je delovni zvezek, poln imenovanih lambd, namenjen poročilu, ki mora biti stabilno, razmislite o zamrznitvi rezultatov s ConvertFormulasToValues, tako da nadaljnji uporabniki vidijo številke namesto funkcij, ki jih morda ne podpirajo

Za intenzivno ponovno izračunavanje so telesa LAMBDA navadni izrazi v grafu odvisnosti in so razporejena kot vsaka druga formula, kar je opisano v prirastnem ponovnem izračunu in grafu odvisnosti. Če vaš model kliče eno imenovano funkcijo čez tisoče vrstic, je strošek telo in ne mehanizem klica, veljajo pa iste optimizacijske smernice kot za katero koli ponovljeno formulo

HotXLS je izvorna komponenta za preglednice za Delphi in C++Builder, ki bere in zapisuje XLS, XLSX in ODS brez Excela ali kakršne koli avtomatizacije Officea. Formulski pogon, definirana imena in API za ponovni izračun so dokumentirani na strani izdelka HotXLS