440 likes | 575 Views
Optimización automática de programas. Tema 3: Funciones, tipos y expresiones 3.1. Intérpretes, compiladores y tiempos de ejecución 3.2. El λ -cálculo sin tipos 3.3. Tres mini-lenguajes de programación. 3.1. Intérpretes, compiladores y tiempos de ejecución. Semántica operacional
E N D
Optimización automática de programas Tema 3: Funciones, tipos y expresiones 3.1. Intérpretes, compiladores y tiempos de ejecución 3.2. El λ-cálculo sin tipos 3.3. Tres mini-lenguajes de programación Germán Vidal
3.1. Intérpretes, compiladores y tiempos de ejecución • Semántica operacional • describe las normas que rigen la ejecución de un programa • similar a la idea de intérprete • Lenguajes de programación • expresamos el “significado” de un programa (escrito en el lenguaje L) como una función que, a partir de una entrada, devuelve una salida: [[p]]L : input output Germán Vidal
Intérpretes y compiladores • Intérpretes • definición ecuacional: [[p]]S [input] = [[int]]L [p,input] • notación: • Compiladores • definición ecuacional: [[p]]S [input] = [[[[comp]]L [p]]]T [input] • notación: S L S T L Germán Vidal
Tiempos de ejecución • Estimación: suele usarse el número de operaciones “elementales”: • acceso al valor de una variable • llamada a función • pattern-matchings • operaciones aritméticas, etc, etc • por ejemplo, el coste de evaluar la expresión x + (2 * y) sería de 5 operaciones elementales • (acceso a “x”, acceso a “y”, recuperar valor de una constante, operación “+” y operación “*”) • notación: tp(d1,…,dn) Germán Vidal
Tiempos de ejecución de un intérprete • El valor de una expresión, e.g., x + 2 * y, depende del valor de sus variables • consideramos un “entorno” que almacena el valor actual de cada variable del programa: env: Variable Value • así, la función que evalúa una expresión sería: eval: Expression x Environment Value Germán Vidal
Tiempos de ejecución de un intérprete • En general, existen 2 alternativas para evaluar las expresiones de un lenguaje: • por sustitución: se reemplazan todas las variables por sus valores • mediante entorno: se mantienen los valores en el entorno y sólo se reemplazan las variables por sus valores cuando es necesario Germán Vidal
Ejemplo data exp = Num int | Var string | Add exp exp | Mul exp exp eval (Num n) env = n eval (Var v) env = env v eval (Add e1 e2) env = (eval e1 env) + (eval e2 env) eval (Mul e1 e2) env = (eval e1 env) * (eval e2 env) E.g., evaluamos x+(2*y) con env = [x5, y7]: eval (Add (Var “x”) (Mul (Num 2) (Var “y”))) env (eval (Var “x”) env) + (eval (Mul …) env) (env “x”) + (eval (Mul (Num 2) (Var “y”)) env) 5 + (eval (Mul (Num 2) (Var “y”)) env) 5 + ((eval (Num 2) env) * (eval (Var “y”) env)) 5 + (2 * (eval (Var “y”) env)) 5 + (2 * (env “y”)) 5 + (2 * 7) 5 + 14 19 Germán Vidal
Sobrecarga de interpretación • Si exp = x + (2 * y), entonces • texp() = 5 • tint(exp) = 9 (contad el número de ) • En general, tint(p,d1,…,dn) = α * tp(d1,…,dn) siendo α la llamada “sobrecarga de interpretación” • su valor, para intérpretes reales, oscila entre 3 y 200, dependiendo del lenguaje… Germán Vidal
3.2. λ-cálculo sin tipos • Alonzo Church se preguntaba… • ...qué funciones matemáticas se pueden computar de forma mecánica (i.e., mediante un algoritmo)? • Su respuesta fue: aquéllas que se pueden especificar en el λ-cálculo (creado por él, 1930) • El λ-cálculo constituye la base teórica de casi todos los lenguajes funcionales • Lisp, Scheme, ML, Haskell, etc Germán Vidal
Notación λ para las funciones • Notación básica: λx.exp (ó λx exp ó \x -> exp) • Se trata de una función “anónima” (sin nombre) con un solo parámetro, x, cuyo cuerpo es exp • Por ejemplo, (λx.x+1) 1 = 2 • Si hay varios parámetros, podemos usar: λ(x1,x2,…,xn).exp λx1.λx2.….λxn.exp • También se pueden indicar los tipos: λ(x1:A1,x2:A2,…,xn:An).exp Germán Vidal
Ejemplos square = \x -> x*x suma = \x,y -> x+y k = \m,n -> (m+n, m-n) twice = \f -> (\x -> f(f(x))) add = \x -> (\y -> x+y) ... Germán Vidal
Sintaxis del λ-cálculo (extendido) <Lam> ::= <Constant> | <Var> | <Lam> <Lam> | \<Var> -> <Lam> | if <Lam> then <Lam> else <Lam> | <Op> <Lam> ... <Lam> donde <Op> es un operador básico (e.g. +, -, *, ...) Germán Vidal
Evaluación de λ-expresiones • Idea básica: • reescribimos (toda o parte de) la expresión inicial mediante una serie de reducciones hasta que se alcanza un valor • usamos la notación: M P, para indicar que M se reduce a P Germán Vidal
Algunos conceptos previos… • Ocurrencia ligada de una variable: • una ocurrencia de la variable x está ligada en una expresión si aparece dentro del ámbito de una lambda • e.g., x está ligada en \x -> x+1 • Ocurrencia libre de una variable: • si no está ligada, • e.g., x es libre en \y -> x+y Germán Vidal
Algunos conceptos previos… • Variable libre: • una variable x es libre en una expresión si existe al menos una ocurrencia libre de x • e.g., x es libre en \y -> x (\x -> x) pero no en \y -> (\x -> x) • Sustitución: • son funciones finitas que denotan la asignación de expresiones a variables libres • así, [N/x]M denota la expresión M en la que las ocurrencias de la variable x se han sustituido por N • también se suele usar M[x/N] o, más a menudo, M[x N] (nosotros usaremos la última opción!) Germán Vidal
Ejercicio 3.1 • Identifica las ocurrencias de variables libres y ligadas en las siguientes expresiones • para las ligadas, indica la lambda que le afecta 1.- (x (\x -> (\x -> x x)) x) 2.- (x (\x -> (\x -> x) x) x) 3.- \h->(\x->h (x x)) (\x->h (x x)) Germán Vidal
Reglas de reducción • α-conversión: \x -> M \y -> M[x y] donde y no debe ser libre en M • Se emplea para renombrar las variables y evitar el “problema de la captura de nombres de variables” (luego lo vemos…) Germán Vidal
Reglas de reducción • β-reducción: (\x -> M) N M[x N] • Representa la aplicación de una función a un argumento • Restricción: • ninguna variable libre de N puede convertirse en ligada tras el paso de β-reducción (problema de la captura de nombres de variables) • se puede evitar con un paso de α-conversión (luego vemos un ejemplo..) Germán Vidal
Reglas de reducción • δ-reducción: op a1 a2 ... an b si b es el resultado de aplicar el operador op sobre las constantes a1, a2, …, an • E.g., (+ 5 6) 11 • Restricción: • los argumentos debe ser constantes Germán Vidal
Reglas de reducción • Reducción de condicional: if true then M else N M if false then M else N N Germán Vidal
Reglas de reducción • Reducción en contextos: ...M... ...N... si M N • Significa que es correcto reducir una parte de una expresión, dejando el resto tal cual Germán Vidal
Reglas de reducción • Reducción repetida: M1 M3si M1 M2 y M2 M3 • Básicamente, coincide con la propiedad transitiva… • También lo denotaremos así: M1 M2 M3 ... Germán Vidal
Ejemplo (\x -> ((\x -> x+x)5)+x+(\x -> x*x)3)4 (\x -> ((5+5)+x+(\x -> x*x)3)4 (\x -> (10+x+(\x -> x*x)3)4 10+4+(\x -> x*x)3 10+4+(3*3) 14+(3*3) 14+9 23 Germán Vidal
Ejercicio 3.2 • Usad las reglas de α- y β-reducción para reducir las siguientes expresiones 1. x ((\y -> x) z) 2. (\x -> x y) (\z -> z) 3. (\y -> (\z -> z y)) (\k -> z) 4. (\x -> (\y -> x y) x) (\x -> z) 5. (\f-> (\g -> (\x -> f x (g x)))) (\x->(\y->x))(\x->(\y->x)) a Germán Vidal
Concepto de redex • Un redex (reducible expression) es: • cualquier expresión que se puede reducir mediante una β-reducción, una δ-reducción o una reducción de condicional • Un redex top-level es: • un redex que no se encuentra dentro del ámbito de una lambda • Una expresión está en weak head normal form (whnf) si • es una constante, e.g., 17 • una función, e.g., \x -> M • una variable libre, e.g., x • o la aplicación de una variable libre a una expresión, e.g., x M Germán Vidal
Concepto de redex • Si una expresión está en whnf • no tiene top-level redexes • Existen expresiones que nunca alcanzan la whnf • e.g., (\y -> y y) (\y -> y y) • Ejemplo de captura de nombres: (\x -> 2 + (\y -> x + y) 5) (y + 1) 2 + (\y -> (y + 1) + y) 5) 2 + (5 + 1) + 5 ... 13 Error! Germán Vidal
Evaluación CBV y CBN • Aunque nos limitemos a la reducción de top-level redexes, existen 2 posibilidades: • reducir primero “el argumento” de una función antes de su evaluación (CBV, call by value) • reducir primero la aplicación de una función (CBN, call by name) Germán Vidal
Evaluación CBV y CBN • CBV: • para aplicar una β-reducción a (\x -> N) M, antes hay que reducir la expresión M a whnf • CBN: • podemos reducir (\x -> N) M sin necesidad de reducir previamente M a una whnf Germán Vidal
CBV vs CBN • En principio, CBN es preferible porque se cumple la “propiedad de completitud”: • si existe alguna posibilidad de reducir M a una whnf P, entonces la estrategia CBN reduce M a P • probad: (\x -> 1) ((\y -> y y) (\y -> y y)) • Sin embargo, CBV puede ser más eficiente que CBN! • probad a reducir, e.g., (\x -> x + x + x) (2+2) • La mejor solución: usar CBN con “sharing”… (base de Haskell, LML, etc) Germán Vidal
Ejercicio 3.3 • Encontrad 2 expresiones M y N tales que • M se evalúe más rápido con CBV que con CBN • N se evalúe más rápido con CBN que con CNV Germán Vidal
Ejercicio 3.5 • Una expresión está en forma normal si no es posible realizar un paso de β-reducción, δ-reducción o reducción de condicional sobre cualquier sub-expresión (no necesariamente un top-level redex) • Encontrad una expresión P sin variables libres cuya reducción a forma normal requiera algún renombramiento mediante α-conversion Germán Vidal
3.3 Tres mini-lenguajes de programación • Un intérprete para el λ-cálculo CBV sin tipos • Un intérprete para ecuaciones recursivas de primer orden (CBV) • Un intérprete para un lenguaje imperativo simple Germán Vidal
Un intérprete para el λ-cálculo CBV sin tipos data lambda = Int int // constante | Var string // variable | Abs string lambda // lambda | Apply lambda lambda // aplicación | Op string [lambda] // operador | If lambda lambda lambda // condicional type environment = ([string],[value]) data value = Numb int | Closure lambda environment Germán Vidal
Un intérprete para el λ-cálculo CBV sin tipos // función principal: interpret :: lambda -> value interpret e = eval e ([],[]) // ([],[]) es el entorno “vacío” // función auxiliar: lookup :: a -> ([a],[b]) -> b lookup x (n:ns,v:vs) = if x == n then v else lookup x (ns,vs) Germán Vidal
Un intérprete para el λ-cálculo CBV sin tipos eval :: lambda -> environment -> value eval (Int n) env = Numb n eval (Var x) env = lookup x env eval (Abs x e) env = Closure (Abs x e) env eval (Apply f e) env = let v = eval e env Closure (Abs x e1) (ns,vs) = eval f env in eval e1 (x:ns,v:vs) Germán Vidal
Un intérprete para el λ-cálculo CBV sin tipos eval (Op “+” [e1,e2]) env = let Numb v1 = eval e1 env Numb v2 = eval e2 env in Numb (v1 + v2) // similar para “-”, “*”, etc eval (If c e1 e2) = case (eval c env) of (Numb 1) -> eval e1 env // 1 es True (Numb _) -> eval e2 env // el resto False Germán Vidal
Ejercicio 3.8 • Escribe un intérprete para el λ-cálculo CBN sin tipos Germán Vidal
Un intérprete para ecuaciones recursivas de primer orden (CBV) // prog = // ([nombres_funcion],[([lista_vars],cuerpo)]) type prog = ([string], [([string],expr)]) data expr = Int int // constante | Var string // variable | If expr expr expr // condicional | Call string [expr] // llamada func. | Op string [expr] // operador Germán Vidal
Un intérprete para ecuaciones recursivas… // función principal (value = int): interpret :: prog -> [expr] -> int interpret pgm [args] = let (_, (vars,exp):_) = pgm in eval exp (vars,args) pgm // asumimos que la ejecución comienza con // la primera función, por eso sólo pasamos // los argumentos... Germán Vidal
Un intérprete para ecuaciones recursivas… // función auxiliar: lookup :: a -> ([a],[b]) -> b lookup x (n:ns,v:vs) = if x == n then v else lookup x (ns,vs) // función auxiliar: evlist ::[exp]->([string],[int])->prog->int evlist [] _ _ = [] evlist (e:es) env pgm = (eval e env pgm) : (evlist es env pgm) Germán Vidal
Un intérprete para ecuaciones recursivas… eval ::exp->([string],[int])->prog->int eval (Int n) env pgm = n eval (Var x) env pgm = lookup x env eval (Call f exps) env pgm = let vals = evlist exps env pgm (vars,exp) = lookup f pgm in eval exp (vars,vals) pgm Germán Vidal
Un intérprete para ecuaciones recursivas… eval (Op “+” [e1,e2]) env pgm = (eval e1 env pgm) + (env e2 env pgm) // similar para “-”, “*”, etc eval (If c e1 e2) env pgm = case (eval c env pgm) of 1 -> eval e1 env pgm // 1 es True _ -> eval e2 env pgm // resto False Germán Vidal
Ejercicio 3.9 • Escribe un intérprete para ecuaciones recursivas de primer orden CBN Germán Vidal
Un intérprete para un lenguaje imperativo simple • No lo vemos en clase… (lo podéis encontrar en [Jones, Gomard, Sestoft 93]) Germán Vidal