Professional Documents
Culture Documents
IntLT.t;
IntLT.t * IntLT.t -> bool
DivLt.t;
IntDiv.t * IntDiv.t -> bool
structure
La mandera de externalizarlo es la misma,
mediante identificadores largos:
IntDiv.lt (exp1, exp2) andalso IntDiv.eq (exp3, exp4)
IntLT.lt(3,4)
IntDiv.lt(3,4)
let
open IntDiv
in
lt (exp1, exp2) andalso eq (exp3, exp4)
end
structure
No se pueden utilizar dos estructuras con un
open:
Ejemplo:
let
open IntLT IntDiv
in
...
end
structure
structure StringLT = struct
type t = string
fun compare (c, d) = Char.< (c, d)
fun lt (s, t) = ... Comparacin ...
fun eq (s, t) = ... comparacin
End
let
structure I = IntLT
in
I.lt (exp1, exp2) andalso I.eq (exp3, exp4)
end
structure
structure PersQueue = struct
type 'a queue = a list * a list
val empty = (nil, nil)
fun insert (x, (bs, fs)) = (x::bs, fs)
exception Empty
fun remove (nil, nil) = raise Empty
| remove (bs, f::fs) = (f, (bs, fs))
| remove (bs, nil) = remove (nil, rev bs)
end
structure
val q = PersQueue.empty
val q' = PersQueue.insert (1, q)
val q'' = PersQueue.insert (2, q)
val (x'', _) = PersQueue.remove q'' (* 2 *)
val (x', _) = PersQueue.remove q' (* 1 *)
let
open PersQueue
in
insert (1, empty)
end
let
structure PQ = PersQueue
in
PQ.insert (1, PQ.empty)
end
Estructura de Colas
structure Queue =
struct exception EmptyQ
val empty = []
fun insert x [] = [x]
| insert x (h::t) = h::(insert x t)
fun add x q = insert x q
fun top [] = raise EmptyQ
| top (h::t) = h
fun pop [] = raise EmptyQ
| pop (h::t) = t
end ;
Estructura de Colas
empty;
Queue.empty;
val q = Queue.add 3 Queue.empty;
val q1 = Queue.add 4 (Queue.add 3 Queue.empty);
Queue.top q1;
val q2 = Queue.pop q1 ;
structure Q = Queue ;
Q.empty;
val q = Q.add 3 Q.empty;
Estructura de Pilas
structure Stack =
struct exception EmptyS
val empty = []
fun insert x [] = [x]
| insert x (h::t) = h::(insert x t)
fun add x s = insert x s
fun top [] = raise EmptyS
| top (h::t) = h
fun pop [] = raise EmptyS
| pop (h::t) = t
fun pops x = rev(pop (rev (x)));
fun tops x = top ( rev (x));
end ;
Estructura de Pilas
empty;
Stack.empty;
val s = Stack.add 3 Stack.empty;
val s1 = Stack.add 4 (Stack.add 3 Stack.empty);
Stack.top q1;
val s2 = Stack.pops s1 ;
structure S = Stack ;
S.empty;
val s = S.add 3 S.empty;
Estructura de pilas
structure Stack =
struct exception EmptyStack
type a stack = a list
val empty = []
val push = op ::
fun pop [] = NONE
| pop ( tos :: rest ) = SOME tos
end;
Estructura de Arboles
EmptyT;
Node;
val e = EmptyT;
val t1 = Node(3,EmptyT,EmptyT) ;
val t2 = Node(5,EmptyT,EmptyT) ;
val t3 = Node(10,t1,t2) ;
val t4 = Node(4,t3,EmptyT);
Ejercicio
Construir un rbol balanceado ordenado t6
que contenga la siguiente forma y que este
construido en una sola lnea de comando:
6
4 8
3 5 7 9
Operaciones en rboles binarios.
Se aplican varias operaciones primitivas a un
rbol binario.
Si p es un apuntador a un nodo nd de un rbol
binario:
1.La funcin info(p) regresa el contenido de nd.
2.La funcin left(p) regresa un apuntador al hijo
izquierdo de nd.
3.La funcin right(p) regresa un apuntador al hijo
derecho de nd.
Estructura de arbol
fun newTree n = Node(n,EmptyT,EmptyT);
masUno (2*3)
aplicamos *
masUno 6
aplicamos masUno
6+1
aplicamos +
7
Alternativamente podemos evaluar la misma
expresin pero aplicando las funciones en el orden
inverso
masUno (2*3)
aplicamos masUno
(2*3) + 1
aplicamos *
6+1
aplicamos +
7
No importa el orden en que apliquemos las
funciones vamos a llegar al mismo resultado
final. Esto no solo vale para ejemplos sencillos
sino que se cumple siempre en Haskell.
Esta propiedad no se cumple en la mayora de
los "lenguajes imperativos" (Pascal, C,
Smalltalk, Java, C#, etc.), veamos un ejemplo
en Smalltalk:
Si tenemos la expresin n + (n := 1)
Si tenemos la expresin n + (n := 1) y n empieza
apuntando a 0.
Si empezamos a evaluar de izquierda a derecha
n + (n := 1)
aplicamos n
0 + (n := 1)
aplicamos :=
0+1
aplicamos +
1
Si empezamos a evaluar de derecha a izquierda
n + (n:= 1)
aplicamos :=
n+1
aplicamos n
1+1
aplicamos +
2
Por ende, est evaluacin nunca terminara.
Sabiendo que
fst (x,_) = x
Consideremos la expresin fst (0,inf)
Usando la estrategia call-by-value
fst (0,inf)
aplicamos inf
fst (0, 1 + inf )
aplicamos inf
fst (0, 1 + (1 + inf) )
aplicamos inf
fst (0, 1 + (1 + (1 + inf) ) )
aplicamos inf
...
Usando call-by-value la evaluacin de la
expresin no termina.
Usemos call-by-name:
fst (0,inf)
aplicamos fst
0
Lazy Evaluation
Si tenemos la siguiente definicin
alCuadrado x = x * x
Vamos a evaluar la expresin alCuadrado (2*3)
usando call-by-value
alCuadrado (1+2)
aplicamos +
alCuadrado 3
aplicamos alCuadrado
3*3
aplicamos *
9
Ahora vamos a evaluar la misma expresin usando
call-by-name
alCuadrado (1+2)
aplicamos alCuadrado el primer +
(1+2) * (1+2)
aplicamos el +
3 * (1+2)
aplicamos el *
3*3
aplicamos el *
9
Llegamos la mismo resultado pero en el segundo
ejemplo realizamos una reduccin ms (4 reducciones
vs 3 reducciones).
Con call-by-name la expresin (1+2) se evalu dos
veces.
Corolario: cuando usamos call-by-value los parmetros
son evaluados una y solo una vez; cuando usamos call-
by-name el mismo parmetro puede llegar a ser
evaluado ms de una vez.
Para evitar este embrollo en vez de tener la expresin
(1+2) vamos a tener un "puntero a la expresin"
llammoslo p.
Para evitar este quilombo en vez de tener la
expresin (1+2) vamos a tener un "puntero a la
expresin" llammoslo p.
alCuadrado (1+2)
aplicamos alCuadrado
let p = (1+2) in p * p
aplicamos +
let p = 3 in p * p
aplicamos *
9
Cualquier reduccin que se haga en una expresin
se va a conocer automticamente por los punteros
a dicha expresin.
Al uso de punteros para compartir expresiones que
representan la mismo parmetro lo vamos a llamar
Sharing.
Al uso de la estrategia call-by-name ms el Sharing
lo vamos a llamar Lazy Evaluation
(esta es la estrategia que usa Haskell).
El Sharing nos asegura que usar Lazy Evaluation
nunca requiera ms pasos que la estrategia call-by-
value.
A una expresin que consta de una funcin
aplicada a uno o ms parmetros y que puede
ser "reducida" aplicando dicha funcin la
vamos a llamar Redex (Reducible Expression).
Se le dice reduccin al hecho de aplicar la
funcin no necesariamente vamos a obtener
una expresin "ms corta" como veremos ms
adelante.
Consideremos la funcin mult que tiene como
dominio una tupla de 2 nmeros
mult (x,y) = x * y
Si queremos reducir la expresin mult (1+2,2+3)
est expresin contiene 3 redexs:
1. 1+2 (la funcin + aplicada a 2 parmetros)
2. 2+3 (la funcin + aplicada a 2 parmetros)
3. mult (1+2,2+3) (la funcin mult aplicada a 1 parmetro
que es una tupla)
Si queremos evaluar la expresin qu estrategia
usamos?
De adentro hacia afuera
call-by-value
Una de las estrategias ms comunes es
comenzar desde adentro hacia afuera
(innermost evaluation), esta estrategia elige el
redex que est "ms adentro" entendiendo
por esto al redex que no contiene otro redex.
Si existe ms de un redex que cumple dicha
condicin se elige el que est ms a la
izquierda.
Usando esta estrategia las funciones se aplican antes que
los parmetros sean evaluados. Por esto se dice que los
parmetros se pasan por nombre.
inf
aplicamos inf
1 + inf
aplicamos inf (porque + es estricta)
1 + (1 + inf)
aplicamos inf (porque + es estricta)
....
1 + (1 + (1 + (1 + (1 + (1 + .... + inf )))))
De adentro hacia afuera
call-by-value
Vamos al ejemplo Esta estrategia me
mult (1+2,2+3) asegura que los
parmetros de una
aplicamos el primer +
funcin estn
mult (3,2+3)
completamente
aplicamos el + evaluados antes de
mult (3,5) que la funcin sea
aplicamos mult aplicada.
3*5 Por eso se dice que
los parmetros se
aplicamos *
pasan por valor.
15
De afuera hacia adentro
call-by-name
CSE341: Programming
Winter 2013 58
Languages
Evaluacin Perezosa
La evaluacin perezosa consiste, como
siempre que hablamos de pereza, en hacer el
mnimo trabajo posible. Es decir, una
expresin no se evala hasta que realmente
se necesita.
Realizar la evaluacin call-by-name y call-by-value de
la siguiente expresion.
fun bad_max (4,3,2) =
if null xs
then 0 (* horrible style; fix later *)
else if null (tl xs)
then hd xs
else if hd xs > bad_max (tl xs)
then hd xs
else bad_max (tl xs);
Funciones de Orden Superior
Funciones ms alto orden
Funcin map
Tiene el tipo
('a -> 'b) -> 'a list -> 'b list.
Ejemplos
fun add x = x + 1;
map add[1,2,3];
fun map f [] = []
| map f (a::l) = (f a)::(map f l)
Funciones ms alto orden
filter:
Tiene el tipo:
('a -> bool) -> 'a list -> 'a list.
Ejemplo
fun div2 x = x mod 2 = 0;
List.filter div2 [1,2,3,4];
Funciones ms alto orden
Fold:
foldl y foldr tienen el siguiente tipo:
('a * 'b -> 'b) -> 'b -> 'a list -> 'b
Ejemplo:
List.foldl (op +) 0 [1,2,3,4,5,6,7,8,9];
List.foldr (op +) 0 [1,2,3,4,5,6,7,8,9];
List.foldl (op ^) "" ["a","bc","def","","ghij"];
List.foldr (op ^) "" ["a","bc","def","","ghij"];
Ejercicio
Investigar el tipo de dato que se construye, o
sea la firma de las funciones y cual finalidad
de las siguientes funciones predefinidas:
Null, length,last,getItem, nth, take , drop, concat,
revAppend, app, map, mapPartial, find, filter,
partition, foldl, foldr, exists, all, tabulate, collate;
Funciones Math
Funciones de orden superior
Una funcin es de orden superior si toma una
funcin como argumento o devuelve una
funcin como resultado.
Ejemplo
fun doble x = x + 2;
fun triple2 x = x * 3;
fun triple f x = f(x) * 3;
fun doble2 f f1 x = f(x) * f1(x);
fun aplica3 f = f(3);
fun fact(n) = if n=0 then 1 else n*fact(n-1);
aplica3(fact);
aplica3 (fn x => x+16);
fun funcion1 x = 2 * x;
fun funcion2 x = x * 3;
fun funcion3 (f1, f2) x = (f1 x, f2 x);
funcion3 (funcion1,funcion2) 3;
funcion3 (funcion1, funcion1) 3;
funcion3 (funcion2,funcion2) 3;
fun doble x = x * 2;
map doble [1,2,3,4];
fun mimap f nil = nil
| mimap f (x::xs) = (f x) :: (mimap f xs);
fun mulc x y = x* y;
mimap (mulc 2) [1,2,3,4];
Realice una funcin even donde aplique lo
siguiente:
mimap even [1,2,3,4,5];
[False,True,False,True,False]