Definiciones¶
Un procedimiento permite usar segmentos de código de forma repetida, sin tener que escribirla de nuevo, abstracción procedural.
Para agregar a la gramatica los procedimientos tenemos:
(expresion ("proc" "(" (separated-list identificador ",") ")" expresion) proc-exp)
Esto me permite escribir cosas como
let
f = proc(x,y) +(x,y)
...
Tengo que evaluar los procedimientos, para esto se agrega la siguiente producción a la gramática
(expresion ("(" expresion (arbno expresion) ")") app-exp)
Para definir un procedimiento vamos a incluir una clausura, que permite almacenar el ambiente donde el procedimiento fue creado
(define-datatype procval procval?
(closure (lid (list-of symbol?))
(body expresion?)
(amb-creation ambiente?)))
En el evaluador tenemos
(proc-exp (ids body)
(closure ids body amb))
Cuando creo una clausura, retorno un TAD tipo closure que almacena los ids, el cuerpo y el ambiente fue creado
proc (x,y) +(x,y)
#(struct:closure
;Lista de IDs
(x y)
; Expresion o cuerpo
#(struct:prim-exp #(struct:sum-prim) (#(struct:var-exp x) #(struct:var-exp y)))
; Ambiente donde fue creado
#(struct:ambiente-extendido (x y z) (4 2 5) #(struct:ambiente-extendido (a b c) (4 5 6) #(struct:ambiente-vacio))))
Y ahora tenemos la evaluacion del procedimiento en el evaluador
(app-exp (rator rands)
(let
(
(lrands (map (lambda (x) (evaluar-expresion x amb)) rands))
(procV (evaluar-expresion rator amb))
)
(if
(procval? procV)
(cases procval procV
(closure (lid body old-env)
(if (= (length lid) (length lrands))
(evaluar-expresion body
(ambiente-extendido lid lrands old-env))
(eopl:error "El número de argumentos no es correcto, debe enviar" (length lid) " y usted ha enviado" (length lrands))
)
))
(eopl:error "No puede evaluarse algo que no sea un procedimiento" procV)
)
)
)
)
Ejemplo¶
Considere el ambiente inicial vacio
let
a = proc(x,y) +(x,y)
b = 10
c = 20
in
let
f = proc(g,x,y) (g x y)
k = 30
in
(f a +(b,k) +(c,k))
graph TD
A["empty-env"] --> B["env0
a b c
closure('(x,y) .. empty-env) 10 20"]
B --> C["env1
f k 30
closure('(g x y) ... env0)"]
B --> D["envf
g x y
closure('(x,y) .. empty-env) 40 50"]
A --> E["envg
x y
40 50"]
- Sobre env1 vamos a evaluar (f a +(b,k) +(c,k)) al evaluarlo, (closure('(g x y) ... env0) closure('(x,y) .. empty-env) 40 50) aqui tenemos dos partes
- Rator closure('(g x y) ... env0)
- Rands (list closure('(x,y) .. empty-env) 40 50)
- Se va generar un ambiente extendido con los datos del rator, extendiendo de env0
- Sobre envf voy a evaluar (g x y), que es (g 40 50), g es closure('(x,y) .. empty-env) , al evaluar implica que debo hacer un ambiente con x,y extendiendo del ambiente vacio
- Sobre envg evaluo +(x,y), estos valen 40 y 50, dandonos 90
- Por lo tanto el valor final es 90
Ejercicio¶
Suponga ambiente inicial (x,y,z) (1 2 3)
let
f = proc(x,y) +(x,y,z)
g = proc(a,b) +(a,b,x,y,z)
in
let
k = let x = (f x y) in +(x,y)
j = let f = proc(a) +(a,x) in (f y)
in
+(k,j)
graph TD
A["empty-env"] --> B["env0
x y z
1 2 3"]
B --> C["env1
f g
closure('(x,y) ... env0) closure('(a,b) .. env0)"
]
C --> D["env2
k j
8 3"]
C --> E["envkx
x
6"]
B --> F["envf1
x y
1 2"]
C --> G["envj
f
closure('(a) ...env1)"]
C --> H["envjf
a
2"]
- Evaluamos (f x y) en env1, (f g
closure('(x,y) ... env0) 1 2), esto implica extender un ambiente desde env0 - Evaluamos \(+(x,y,z)\) -> \(+(1,2,3)\) nos da 6
- Evaluamos \(+(x,y)\) en envkx \(+(6,2)=8\)
- Sobre envj evaluamos (f y) que es (closure('(a) ...env1) 2) esto nos produce otro ambiente que extiende de env1
- Sobre envjf voy a evaluar \(+(a,x)\) lo que da \(+(2,1)=3\)
- En total 8+3 = 11