You are on page 1of 74

SML/NJ

Definiendo Valores con funciones


annimas
Puede ser de dos formas:
fun suma x = x +1;
val inc = suma;
o de esta forma simplificada
val inc = fn (x) => x + 1;
Definiendo Valores con funciones
annimas
Ejemplos
(fn x => x+1);
(fn x => x+1) 3;
((fn x=>x+2) 4, (fn x=>x*3) 7);
val p = (fn (x,y) => x + y, fn (x,y) => x - y);
#1(p)(2,3);
#2(p)(2,3);
fun make_addx(x) = fn(y) => y + x;
fun make_aff 3;
Fun make_add 3 3;
Structure
En SML se maneja un tipo de modularidad:
Structure
Consta de una secuencia de declaraciones que
comprenden los componentes del mdulo
Puede estar unido a una variable de estructura
Los componentes se acceden mediante
identificadores largos o caminos (paths).
Pueden abrirse para incorporar todos sus
componentes en el medio
Structure
Ejemplos:
structure IntLT=struct
type t=int
val lt=(op <)
val eq=(op =)
end
structure IntDiv = struct
type t = int
fun lt (m, n) = (n mod m = 0)
val eq = (op =)
end
Structure
Solo podemos acceder a los componentes
mediante Identificadores o nombres cualificados:

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

Un rbol binario es un conjunto finito de elementos, el cual


est vaco o dividido en tres subconjuntos separados:
El primer subconjunto contiene un elemento nico
llamado raz del rbol.
El segundo subconjunto es en s mismo un rbol binario y
se le conoce como subrbol izquierdo del rbol original.
El tercer subconjunto es tambin un rbol binario y se le
conoce como subrbol derecho del rbol original.
Estructura de arbol
datatype bintree = EmptyT
| Node of (int * bintree * bintree) ;

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);

fun left_child (Node (_,t,_)) = t;


fun right_child (Node (_,_,t)) = t;
fun root_value (Node (v,_,_)) = v;
left_child t3;
En la construccin de un rbol binario son
tiles las operaciones:
makeTree(x) crea un nuevo rbol que consta de un
nodo nico con un campo de informacin x, y
regresa un apuntador a este nodo.
setLeft(p, x) crea un nuevo hijo izquierdo de
node(p) con el campo de informacin x.
setRight(p, x) crea un nuevo hijo derecho de
node(p) con el campo de informacin x.
Inserta Nodo en arbol binario
fun insert n EmptyT = Node(n,EmptyT,EmptyT)
| insert n (Node (m,t1,t2)) = if (n < m) then
Node (m, (insert n t1), t2) else
Node (m, t1, (insert n t2)) ;
Insertando nodos en rboles
Ejercicio
Construir un rbol utilizando la funcin
insert con los siguientes nmeros:
6,7,8,4,5,3,9.
Haga una serie de pasos para construir el
rbol
Hacer una prueba de escritorio de cmo
funciona dicha funcin
Dibuje como quedara el rbol
Bsqueda de un dato en rbol binario
fun lookup n EmptyT = false
| lookup n (Node (m,t1,t2)) = if m=n then true
else if (lookup n t1) then true
else if (lookup n t2) then true
else false ;
Ejercicio
Hacer una prueba de escritorio con la funcin
anterior para localizar el nmero 9 y el
nmero 1 en el rbol construido
anteriormente.
Ejercicio
Investigar o realizar las funciones de
ordenamiento preorden, postorden e inorden
Realizar un ejemplo para el rbol construido
anteriormente
Los operadores and y or funcionan como lazy
evaluation, aunque en realidad a este
comportamiento con los operadores lgicos se
le llama evaluacin mnima o evaluacin de
cortocircuito ( short circuit evaluation ).
Veamos un ejemplo:
val test = ("a" = "b") or (1=1) or ("c" = "h");
val test = ("a" = "b") and (1=1) and ("c" = "h");
Evaluacin perezosa
para que se utiliza?
A pesar del trmino perezosa, realmente este tipo de
funcionamiento puede ser ms costoso, porque
requiere llevar un control del estado de las
expresiones, para saber si estn ya evaluadas o es la
primera vez que se encuentran. Cuando no se utiliza no
tenemos que llevar este tipo de control, porque las
expresiones se evaluan siempre.
Adems implica una cierta perdida de control porque
no es evidente qu parte del cdigo se va a ejecutar en
cada caso. Esto puede suponer un problema cuando
necesitamos controlar estados y puede llevar a errores
difciles de encontrar.
JavaScript lo utiliza slo puntualmente, pero hay
lenguajes, como Haskell, que lo utilizan
siempre. Podemos ver la diferencia con una
funcin que tiene otras funciones como
parmetros (funcin de orden superior):
function ( func1(), func2(), func3() ) {
...
}
Estrategias de Evaluacin
Introduccin
La operacin que realizamos en los lenguajes
funcionales es aplicar funciones, la idea del
tema que vamos a tratar a continuacin es
saber qu se tiene que tener en cuenta para
determinar el orden en en que aplicarn las
funciones de una expresin.
Estrategias de evaluacin
Ejemplo: masUno x = x + 1

La expresin masUno (2*3) puede ser evaluada de la


siguiente forma:

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.

Nota: Hay que tener en cuenta que muchas funciones que


ya vienen con Haskell requieren que sus parmetros estn
evaluados antes de que la funcin sea aplicada, incluso
cuando usamos la estrategia "de afuera hacia adentro".

A las funciones que cumplen con esta propiedad las vamos


a llamar funciones estrictas. Funciones estrictas que nos
van a interesar a nosotros:
Operaciones aritmticas (+,*,/,etc.)
Pattern-Matching (sobre listas, tuplas, etc.)
Evaluaciones que no terminan
Tengan en cuenta la siguiente definicin
inf = 1 + inf

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

Otra de las estrategias ms comunes es


comenzar desde afuera hacia adentro
(outtermost evaluation), esta estrategia elige
el redex que est "ms afuera" entendiendo
por esto al redex que no esta contenido en
otro redex. Si existe ms de un redex que
cumple dicha condicin se elige el que est
ms a la izquierda.
Vamos al ejemplo
mult (1+2,2+3)
aplicamos el primer + (Si
aplicamos mult(1+2) * (2+3) seguimos lo que dijimos
3 * (2+3) arriba deberamos
aplicar primero el * pero
aplicamos + vamos a explicar porque
3*5 no lo hacemos ms
aplicamos * abajo)
15
Ejercicio:
Realizar una funcin countup que al darle dos
parmetros (from, to) enteros obtengamos una
lista que vaya del primer parmetro al segundo
de 1 en 1 en forma ascendente
Realizar una funcin countdown que al darle
dos parmetros (from, to) enteros obtengamos
una lista que vaya del primer parmetro al
segundo de 1 en 1 en forma descendente
Ejercicio
Proporcionando una lista de nmero,
determinar como resultado el nmero
mximo. Considerar tambin si la lista que se
proporciona es nula.
fun countdown(from: int, to : int ) =
if from = to then
to::[]
else
from :: countdown(from-1,to);

fun countup(from: int, to : int ) =


if from = to then
to::[]
else
from :: countdown(from+1,to);
CSE341: Programming
Winter 2013 56
Languages
fun bad_max (xs : int list) =
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);
CSE341: Programming
Winter 2013 57
Languages
Countup(1,5);
Countdown(5,1);
let x = bad_max(contdown(5,1);
let y = bad_max (countup(1,5);

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 lista x = [x];


map lista [1,2,3];

Map podria definirse as

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]

Realice una funcin incr donde se introduzca una


lista y saque cuales son pares y cuales no;
Incr [1,2,3,4,5];
[False,True,False,True,False];
fun incr (list) = map (even, list);
Escribe una funcion comp : que reciba dos
funciones y un valor y devuelva la
composicin de las funciones aplicada al
tercer argumento.
Por ejemplo: comp cuad (enesimo 3)
[2,3,4,5];
val it = 16 : int
Hacer una funcin filter donde una lista y una
funcin que determine que es lo que se va a
filtrar en la lista
filter even [1,3,5,4,6,1];
[4,2,6]
foldr
Existe una funcin foldr que recibe una
funcin binaria, un valor y una lista. El
resultado es que desenvuelve la lista e
intercambia el operador :: por la funcion y nil
por el segundo argumento. Es decir, dada una
lista l = x1 :: . . . :: xn :: nil, la expresin foldr f x
l equivale a x1 f . . . f xn f x (escribiendo f en
notacin infija para mayor claridad).
Escriba la funcin factorial utilizando foldr.
signature
Es un tipo de estructura
Se describe una estructura mediante la
especificacin de cada uno de sus
componenetes, dando su nombre y su
descripcin
signature
signature ORDERED = sig
type t
val lt : t * t -> bool
val eq : t * t -> bool
end
signature INT_ORDERED = sig
type t = int
val lt : t * t -> bool
val eq : t * t -> bool
end

You might also like