HotXLS evaluerer Excel LAMBDA som en ekte førsteklasses funksjonsverdi. Et definert navn hvis RefersTo-tekst er en LAMBDA, kan kalles ved navn som =MyFunc(5), en klosur bundet inne i LET kan kalles som =LET(f, LAMBDA(x, x*2), f(21)), og det leksikalske miljøet fanget på definisjonstidspunktet følger med klosuren. Formelteksten rundtripper ordrett inn i arbeidsboken
Dette er funksjonen som skiller en formelmotor fra en formelparser. Alt før LAMBDA kunne evalueres ved å gå gjennom et tre av verdier. LAMBDA krever en omfangsstakk, og når du har en omfangsstakk, begynner en hel klasse brukerskrevet regnearklogikk å fungere i Delphi-applikasjonen din i stedet for bare i Excel
Hvorfor stopper de fleste ikke-Excel-motorer ved LAMBDA-nøkkelordet?
Fordi en klassisk regnearkevaluator har nøyaktig én type verdi: et tall, en streng, en boolsk verdi, en feil, eller en referanse til celler som holder disse. Det finnes ingen plass å legge en funksjon. Da Excel 365 introduserte LAMBDA, la det til en verditype som bærer parameternavn, et kroppsuttrykk, og bindingene synlige der den ble skrevet. En motor uten den typen kan analysere LAMBDA(x, x*2) og lagre teksten, men i det øyeblikket en celle prøver å kalle den, er det ingenting å kalle
HotXLS implementerer det manglende stykket som en klosurverdi pluss en omfangsstakk under kjøring. Å kalle en klosur legger dens fangede miljø på stakken, deretter legger argumentverdiene på stakken under parameternavnene, evaluerer kroppen, og trunkerer stakken tilbake til merket. Den rekkefølgen betyr noe, og neste avsnitt forklarer hvorfor
De tre måtene en LAMBDA kalles på
HotXLS løser opp et kall til et ukjent funksjonsnavn gjennom tre stier, prøvd i rekkefølge, og å vite hvilken som utløses, forklarer de fleste overraskelser. Først, et navn bundet i gjeldende LET- eller LAMBDA-omfang: hvis f er en lokal binding som holder en klosur, anvender f(21) den. Deretter, et arbeidsbokdefinert navn hvis formeltekst begynner med LAMBDA: MyFunc(5) kompilerer det navnets kropp og anvender den. Til sist, den klassiske brukerfunksjonshåndtereren, uendret, for alt de to første stiene ikke krever
En lokal binding som holder noe annet enn en klosur, kan ikke kalles. Bind f til tallet 3 og skriv deretter f(21), og du får en verdifeil, ikke et forsøk på multiplikasjon. Dette er strengere enn et dynamisk språk ville vært, og bevisst så: en skrivefeil som gjør et funksjonskall om til en utilsiktet referanse, er et stille feil svar, som er det verste utfallet en regnearkmotor kan produsere
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
begin
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Model');
// En gjenbrukbar navngitt funksjon, arbeidsbokomfang
Book.DefinedNames.Add('NetOf', 'LAMBDA(amount, rate, amount*(1-rate))');
Sheet.Cells[2, 2].Formula := 'NetOf(1250, 0.19)';
// En klosur bundet og anvendt inne i én formel
Sheet.Cells[3, 2].Formula := 'LET(double, LAMBDA(x, x*2), double(21))';
// Nøstet LET: hver binding er synlig for de som kommer etter den
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;
Hvordan løses skyggelegging når navn kolliderer?
Parametere vinner. Når HotXLS anvender en klosur, legger den det fangede leksikalske miljøet på stakken først og argumentbindingene deretter, slik at en parameter kalt rate skygger for en ytre binding kalt rate og også skygger for en likt stavet kolonnereferanse i den omkringliggende formelen. Denne rekkefølgen er det som gjør en navngitt funksjon trygg å gjenbruke: den som kaller, kan ikke ved et uhell endre hva kroppen betyr ved å ha en likt navngitt binding i omfang
Aritet sjekkes før noe evalueres. Et kall der antall argumenter ikke stemmer med klosurens antall parametere, returnerer umiddelbart en verdifeil, i stedet for å evaluere noen argumenter og deretter feile, noe som holder sideeffektfri evaluering genuint fri for delvis arbeid. Omfangsstakken trunkeres tilbake til inngangsmerket sitt i en finally-blokk, slik at en feil inne i en kropp ikke kan la utdaterte bindinger stå synlige for neste formel
var
Book: TXLSXWorkbook;
Name: TXLSXDefinedName;
begin
Book := TXLSXWorkbook.Create;
try
if Book.Open('customer-model.xlsx') = 1 then
begin
// Inspiser hva brukeren skrev før du stoler på en omregning
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 er ikke lenger delvis
Tidligere HotXLS-utgivelser implementerte LET bare langt nok til å håndtere det vanlige tilfellet med én binding. Den nåværende implementasjonen er fullstendig: hver binding er synlig for alle senere bindinger og for kroppsuttrykket, og nøstet LET kombineres normalt, slik at LET(a, 1, b, a+1, LET(c, b*2, c)) evalueres på samme måte som Excel evaluerer den
Denne fullstendigheten betyr mer enn det høres ut som. LET er måten brukere unngår å regne ut det samme deluttrykket fem ganger i én formel på, så reelle arbeidsbøker bruker det nettopp i de dypt nøstede formene en delvis implementasjon gjør feil. Hvis du tidligere jobbet rundt hull ved å utvide LET-bindinger før evaluering, kan den løsningen fjernes
Komma eller semikolon: begge deler, nå
Formeltekst i HotXLS godtar nå komma som argumentseparator ved siden av det klassiske semikolonet. Dette er ikke en lokalitetsinnstilling; det er en aksepteringsregel i parseren. Det betyr noe fordi formler kommer fra steder du ikke kontrollerer: limt inn fra en supportsak, kopiert ut av dokumentasjon, generert av et skript som skrev ut Excels kanoniske syntaks, importert fra en CSV med formelstrenger
Den praktiske effekten er at både SUM(A1,A2) og SUM(A1;A2) kompilerer. Rundtripping bevarer det kilden brukte, slik at en arbeidsbok du lastet inn, skrives tilbake med sine opprinnelige separatorer i stedet for å normaliseres bak ryggen på brukeren
Hva rundtripper, og hva du bør sjekke
Formelteksten lagres ordrett, så en LAMBDA i et definert navn overlever en last-og-lagre-syklus intakt og åpnes i Excel som den samme funksjonen. En bar LAMBDA lagret som et celleresultat, altså en formel som evaluerer til en klosur snarere enn til en verdi, beholder den eksisterende hopp-over-uten-verdi-atferden: teksten bevares, ingen bufret numerisk resultat oppfinnes for den. Det er det ærlige utfallet, siden det ikke finnes noen skalar å bufre
To vaner er verdt å innføre. Gi navngitte lambdaer arbeidsbokomfang med mindre det finnes en grunn til å la være, fordi en arkbundet funksjon som forsvinner når et ark kopieres, gir en navnefeil et sted langt fra årsaken; omfangsreglene dekkes i definerte navn og arkoverskridende formler. Og når en arbeidsbok full av navngitte lambdaer skal inn i en rapport som må være stabil, bør du vurdere å fryse resultatene med ConvertFormulasToValues, slik at nedstrøms konsumenter ser tall fremfor funksjoner de kanskje ikke støtter
Ved tung omregning er LAMBDA-kropper vanlige uttrykk i avhengighetsgrafen og planlegges som enhver annen formel, som er beskrevet i inkrementell omregning og avhengighetsgrafen. Hvis modellen din kaller én navngitt funksjon på tvers av tusenvis av rader, er kostnaden kroppen, ikke kallmaskineriet, og de samme optimaliseringsrådene gjelder som for enhver gjentatt formel
HotXLS er en native regnearkkomponent for Delphi og C++Builder som leser og skriver XLS, XLSX og ODS uten Excel eller noen Office-automatisering. API-ene for formelmotoren, definerte navn og omregning er dokumentert på HotXLS sin side for Delphi regnearkkomponent