segue dalla parte 12
Upvalue e Closure
Per chi non ha familiarità con i concetti di programmazione funzionale questi termini possono sembrare un po’ oscuri; vediamo di chiarirli con un semplice esempio:
-- definisco una funzione che parte da un numero N e conta alla rovescia
function CreaContatore(N)
local v=N
local function conta(x)
if v>=x then v=v-x end
return v
end
return conta
end
-- creo qualche istanza:
contaDaDieci=CreaContatore(10)
contaDaCento=CreaContatore(100)
print(contaDaDieci(1))
9
print(contaDaCento(1))
99
print(contaDaCento(1))
98
print(contaDaDieci(1))
8
print(contaDaDieci(2))
6
print(contaDaCento(10))
88
osserviamo le variabili N,v che usate dalla funzione interna: non sono locali, ma nemmeno globali… Sono upvalue, ovvero riferimenti che provengono da uno stackframe esterno. Quando una funzione usa variabili definite in uno scope lessicale a livello superiore, Lua provvede a memorizzare lo stato, tecnicamente spostando la gestione degli upvalue dallo stack in una zona di memoria dedicata, perché altrimenti al ritorno della funzione lo stack verrebbe perso. Ogni funzione che usa uno o più upvalue è chiamata closure. Si tratta di una caratteristica molto potente perché ci permette ad esempio di implementare callback e sandbox. Un esempio a puro scopo didattico:
do
local oldExecute=os.execute -- salva vecchia funzione
local function newExec(command)
local a,b=command:find("rm")
if a then
print("NON eseguo: "..command)
else
print("eseguo: "..command)
return oldExecute(command)
end
end
os.execute=newExec
end
os.execute("/bin/ls")
os.execute("/bin/rm prova.txt")
dopo questa ridefinizione, abbiamo creato una versione ‘sicura’ di os.execute: decidiamo noi quali comandi può lanciare l’utente… Nell’esempio, scartiamo qualsiasi cosa contenga “rm”. Proviamo:
$ lua sandbox.lua
eseguo: /bin/ls
coroutine.lua sandbox.lua
NON eseguo: /bin/rm prova.txt