Műszaki cikk

LAMBDA és LET Delphiben: HotXLS formula-lezárások

A HotXLS az Excel LAMBDA-t valódi elsőrangú függvényértékként értékeli ki. Egy definiált név, amelynek RefersTo szövege egy LAMBDA, névvel hívható, mint =MyFunc(5), egy LET-en belül kötött lezárás hívható, mint =LET(f, LAMBDA(x, x*2), f(21)), és a definiáláskor rögzített lexikális környezet a lezárással együtt utazik. A formulaszöveg szó szerint vissza-oda íródik a munkafüzetbe

Ez az a funkció, amely elválasztja a formulamotort egy formulaelemzőtől. Minden, ami a LAMBDA előtt volt, kiértékelhető volt egy értékfa bejárásával. A LAMBDA egy hatókörvermet igényel, és ha egyszer van egy hatókörvermed, a felhasználók által írt táblázatlogika egy egész osztálya elkezd működni a Delphi-alkalmazásodban, nem csak Excelben

Miért állnak meg a legtöbb nem Excel motorok a LAMBDA kulcsszónál?

Mert egy klasszikus táblázatkiértékelőnek pontosan egyfajta értéke van: egy szám, egy string, egy boolean, egy hiba, vagy egy hivatkozás olyan cellákra, amelyek ezeket tartalmazzák. Nincs hova tenni egy függvényt. Amikor az Excel 365 bevezette a LAMBDA-t, hozzáadott egy értéktípust, amely paraméterneveket, egy törzskifejezést és az ott írt helyen látható kötéseket hordoz. Egy motor e típus nélkül képes elemezni a LAMBDA(x, x*2) kifejezést, és eltárolni a szöveget, de abban a pillanatban, amikor egy cella megpróbálja hívni, nincs mit hívni

A HotXLS a hiányzó darabot egy lezárásértékként, plusz egy futásidejű hatókörveremként implementálja. Egy lezárás hívása felnyomja a befogott környezetét, majd felnyomja az argumentumértékeket a paraméternevek alatt, kiértékeli a törzset, és visszavágja a vermet a jelölésig. Ez a sorrend számít, és a következő szakasz elmagyarázza, miért

A három módja, ahogyan egy LAMBDA meghívódik

A HotXLS egy ismeretlen függvénynévre irányuló hívást három úton old fel, sorrendben kipróbálva, és annak ismerete, melyik sül el, megmagyarázza a legtöbb meglepetést. Először: egy az aktuális LET vagy LAMBDA hatókörben kötött név: ha az f egy lezárást tartó lokális kötés, az f(21) alkalmazza azt. Másodszor: egy munkafüzet-szintű definiált név, amelynek formulaszövege LAMBDA-val kezdődik: a MyFunc(5) lefordítja annak a névnek a törzsét, és alkalmazza. Harmadszor: a klasszikus felhasználói függvénykezelő, változatlanul, mindenre, amit az első két útvonal nem igényel

Egy lokális kötés, amely nem lezárást tart, nem hívható. Kösd az f-et a 3-as számhoz, majd írd, hogy f(21), és egy értékhibát kapsz, nem egy szorzási kísérletet. Ez szigorúbb, mint amilyen egy dinamikus nyelv lenne, és ez szándékos: egy elgépelés, amely egy függvényhívást véletlen hivatkozássá alakít, egy csendes rossz válasz, ami a legrosszabb kimenetel, amit egy táblázatmotor produkálhat

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

    // Egy újrafelhasználható nevesített függvény, munkafüzet hatókörben
    Book.DefinedNames.Add('NetOf', 'LAMBDA(amount, rate, amount*(1-rate))');

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

    // Egy lezárás, kötve és alkalmazva egy formulán belül
    Sheet.Cells[3, 2].Formula := 'LET(double, LAMBDA(x, x*2), double(21))';

    // Beágyazott LET: minden kötés látható az utána következők számára
    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;

Hogyan oldódik fel az árnyékolás, amikor a nevek ütköznek?

A paraméterek nyernek. Amikor a HotXLS alkalmaz egy lezárást, először a befogott lexikális környezetet nyomja fel, másodszor az argumentumkötéseket, így egy rate nevű paraméter árnyékol egy rate nevű külső kötést, és árnyékol egy azonos írású oszlophivatkozást is a körülötte lévő formulában. Ez a sorrend teszi biztonságossá egy nevesített függvény újrafelhasználását: a hívó nem tudja véletlenül megváltoztatni, mit jelent a törzs egy hasonló nevű, hatókörben lévő kötéssel

Az aritás ellenőrzésre kerül, mielőtt bármi kiértékelődne. Egy hívás, amelynek argumentumszáma nem egyezik a lezárás paraméterszámával, azonnal értékhibát ad vissza, ahelyett hogy néhány argumentumot kiértékelne, majd meghiúsulna, ami valóban mentessé teszi a mellékhatásmentes kiértékelést a részleges munkától. A hatókörverem egy finally blokkban vágódik vissza a belépési jelölésig, így egy hiba a törzsben nem hagyhat elavult kötéseket láthatóvá a következő formula számára

var
  Book: TXLSXWorkbook;
  Name: TXLSXDefinedName;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open('customer-model.xlsx') = 1 then
    begin
      // Ellenőrizd, mit írt a felhasználó, mielőtt megbíznál egy újraszámításban
      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;

A LET már nem részleges

A korábbi HotXLS kiadások csak addig implementálták a LET-et, hogy kezelni tudják a gyakori, egykötéses esetet. A jelenlegi implementáció teljes: minden kötés látható az összes későbbi kötés és a törzskifejezés számára, és a beágyazott LET normálisan komponálódik, így a LET(a, 1, b, a+1, LET(c, b*2, c)) ugyanúgy kerül kiértékelésre, ahogy az Excel kiértékeli

Ez a teljesség jobban számít, mint ahogy hangzik. A LET az, amivel a felhasználók elkerülik, hogy ugyanazt a részkifejezést ötször újraszámolják egy formulában, így a valódi munkafüzetek pontosan azokban a mélyen beágyazott alakokban használják, amelyeket egy részleges implementáció elront. Ha korábban a hiányosságokat úgy kerülted meg, hogy a LET-kötéseket kiértékelés előtt kibontottad, az a megkerülő megoldás most elhagyható

Vessző vagy pontosvessző: most már mindkettő

A HotXLS formulaszövege most már elfogadja a vesszőt argumentumelválasztóként a klasszikus pontosvessző mellett. Ez nem egy locale-beállítás; ez egy elfogadási szabály az elemzőben. Azért számít, mert a formulák olyan helyekről érkeznek, amelyeket nem irányítasz: egy support jegyből bemásolva, dokumentációból kimásolva, egy olyan szkript által generálva, amely az Excel kanonikus szintaxisát adta ki, vagy formulastringek egy CSV-jéből importálva

A gyakorlati hatás az, hogy a SUM(A1,A2) és a SUM(A1;A2) is lefordul. A vissza-oda íródás megőrzi, amit a forrás használt, így egy betöltött munkafüzet a saját eredeti elválasztóival íródik vissza, ahelyett hogy a felhasználó háta mögött normalizálódna

Mi íródik vissza, és mit ellenőrizz

A formulaszöveg szó szerint kerül tárolásra, így egy definiált névben lévő LAMBDA sértetlenül éli túl a betöltési és mentési ciklust, és ugyanazon függvényként nyílik meg Excelben. Egy meztelen LAMBDA cellaeredményként tárolva, vagyis egy olyan formula, amely egy lezárásra, nem egy értékre értékelődik ki, megtartja a meglévő érték-nélküli-kihagyás viselkedést: a szöveg megőrződik, semmilyen gyorsítótárazott numerikus eredmény nincs kitalálva hozzá. Ez a becsületes kimenetel, mivel nincs skalár, amit gyorsítótárazni lehetne

Két szokást érdemes felvenni. Adj a nevesített lambdáknak munkafüzet hatókört, hacsak nincs okod másra, mert egy munkalap hatókörű függvény, amely eltűnik, amikor egy munkalapot lemásolnak, egy névhibát okoz az októl távol eső helyen; a hatókör-szabályokat a definiált nevek és munkalapok közötti formulák című cikk tárgyalja. És amikor egy nevesített lambdákkal teli munkafüzet egy stabilnak kell lennie jelentés számára készül, fontold meg az eredmények lefagyasztását a ConvertFormulasToValues függvénnyel, hogy a lentebbi fogyasztók számokat lássanak, ne olyan függvényeket, amelyeket esetleg nem támogatnak

Nagy terhelésű újraszámításnál a LAMBDA törzsek közönséges kifejezések a függőségi gráfban, és ugyanúgy vannak ütemezve, mint bármely más formula, amit a inkrementális újraszámítás és a függőségi gráf című cikk ír le. Ha a modelled egy nevesített függvényt hív ezer sorban, a költség a törzs, nem a hívási gépezet, és ugyanaz az optimalizálási tanács vonatkozik rá, mint bármely ismételt formulára

A HotXLS egy natív Delphi és C++Builder táblázatkezelő komponens, amely Excel vagy bármilyen Office automatizálás nélkül olvas és ír XLS, XLSX és ODS fájlokat. A formulamotor, a definiált nevek és az újraszámítási API a HotXLS Delphi táblázatkezelő komponens oldalán van dokumentálva