Laboratorio de Lenguajes de Programación Introducción al lenguaje ML 2a. parte Pedro A. Góngora Luna1 1. Tipos de datos algebraicos 1.1. Tipos enumerados Además de los tipos básicos, tuplas y listas, en ML podemos crear nuestros propios tipos de datos. El forma más simple son los tipos enumerados. Por ejemplo: datatype planeta = Mercurio | Venus | Tierra | Marte | Jupiter | Saturno | Urano | Neptuno; En seguida podemos usar el nuevo tipo y los valores definidos: - val p = Tierra; val p = Tierra : planeta - p; val it = Tierra : planeta - Tierra; val it = Tierra : planeta - val p = Marte; val p = Marte : planeta También, ML sobrecarga automáticamente el operador = para nuestro nuevo tipo: - Mercurio = Jupiter; val it = false : bool - Marte = p; val it = true : bool Podemos usar funciones sobre nuestro tipo usando pattern matching: 1 [email protected] 1 - fun posicion | posicion | posicion | posicion | posicion | posicion | posicion | posicion val posicion = Mercurio = 1 Venus = 2 Tierra = 3 Marte = 4 Jupiter = 5 Saturno = 6 Urano = 7 Neptuno = 8; fn : planeta -> int - posicion Tierra; val it = 3 : int También podemos usar _ en las clausulas como comodı́n: - fun habitable Tierra = true | habitable _ = false; val habitable = fn : planeta -> bool - habitable Tierra; val it = true : bool - habitable Venus; val it = false : bool A los valores Mercurio, Venus, etc., les llamamos constructores. En estos ejemplos, tenemos constructores de aridad 0, pero también podemos tener constructores n-arios. Por ejemplo: - datatype pareja = P of bool * bool; datatype pareja = P of bool * bool - P(true,false); val it = P (true,false) : pareja Y podemos definir funciones sobre este nuevo tipo: - fun p_or (P (false,false)) = false | p_or _ = true; val p_or = fn : pareja -> bool - p_or (P (false,true)); val it = true : bool - p_or (P (false,false)); val it = false : bool 1.2. Tipos recursivos Usando datatype, podemos crear estructuras de datos recursivas. Por ejemplo, un árbol binario de enteros (con información en las hojas): 2 datatype tree = Hoja of int | Nodo of tree*tree; De esta forma, el árbol: 1 o ◦ OOOO OOO ooo o o OOO oo o OOO o o O ooo ◦> ◦> >> >> >> >> >> >> 2 3 4 se representa como: - val x = Nodo ( (Nodo (Hoja 1, Hoja 2)), (Nodo (Hoja 3, Hoja 4)) ); val x = Nodo (Nodo (Hoja #,Hoja #),Nodo (Hoja #,Hoja #)) : tree Y podemos definir funciones recursivas sobre los tipos recursivos: - fun cuentaHojas (Hoja _) = 1 | cuentaHojas (Nodo (n1, n2)) = (cuentaHojas n1) + (cuentaHojas n2); val cuentaHojas = fn : tree -> int - cuentaHojas x; val it = 4 : int 2. Ejercicios 1. Defina una función sumaHojas que calcule la suma de los valores de todas las hojas de un árbol binario. 2. Defina una función profundidad que calcule la profundidad de un árbol binario. 3. Podemos usar tipos recursivos para representar elementos sintácticos de un lenguaje. Considera el lenguaje sencillo de las expresiones aritméticas: AExp ::= n | a + a | a − a | a ∗ a Esta pequeña gramática la podemos escribir en ML ası́: datatype aexp = Lit of int | Sum of (aexp*aexp) | Sub of (aexp*aexp) | Mul of (aexp*aexp); Escribe una función eval que dada una expresión la evalúe y devuelva el resultado. Por ejemplo: - eval (Lit 5); val it = 5 : int - eval (Sum (Lit 3, Lit 5)); val it = 8 : int 3