Tema 6: Programación funcional en Scala Sesión 18: Programación funcional en Scala jueves 7 de abril de 2011 Referencias • Programming in Scala. Martin Odersky, Lex Spoon, Bil Venners. Ed. Artima. • Programming Scala. Dean Wampler, Alex Payne. Ed. O'Reilly. • Scala by Example. Martin Odersky. November 2010. • A Scala Tutorial for Java programmers. Michel Schinz, Philipp Haller. November 2010. • The Scala Language Specification. Martin Odersky. November 2010. jueves 7 de abril de 2011 Expresiones y funciones simples en el intérprete • El intérprete analiza las expresiones, realiza las inferencias de tipos necesarias y evalua la expresión: scala> 5 + 2 * 3 scala> 5 + 2.0 * 3 scala> "hola" + " mundo!" jueves 7 de abril de 2011 Variables mutables e inmutables • Scala diferencia entre variables inmutables (declaradas con val y variables mutables var) • Es recomendable utilizar siempre que podamos variables inmutables (val), esto refuerza el carácter funcional de nuestros programas • Las variables son estrictamente tipeadas, aunque no es necesario declarar el tipo porque Scala puede inferirlo jueves 7 de abril de 2011 Variables mutables e inmutables scala> val msg = "Hola mundo!" msg: java.lang.String = Hola mundo! scala> var saludo = "Hola mundo!" saludo: java.lang.String = Hola mundo! scala> saludo = "Hasta luego!" saludo: java.lang.String = Hasta luego! scala> saludo = 3.0 <console>:5: error: type mismatch; found : Double(3.0) required: java.lang.String jueves 7 de abril de 2011 Variables mutables e inmutables • ¡Cuidado! El intérprete permite declarar varias veces la misma variable. Cada ejecución de una nueva sentencia crea un nuevo ámbito en el que se define la nueva variable. Las variables anteriores se pierden. Esto no es correcto en un programa; una variable sólo se puede declarar una vez. • Las variables declaradas con val no pueden ser reasignadas. Esto no impide que cambie el valor del objeto que contienen (en el caso de estar en el paradigma imperativo). Ejemplo: scala> val a = Array(1,0,0) a: Array[Int] = Array(1, 0, 0) scala> a(1) = 1 scala> a res42: Array[Int] = Array(1, 1, 0) jueves 7 de abril de 2011 Tipos de datos básicos • Aunque hablemos de tipos de datos, en Scala todos los tipos de datos son clases • Algunos tipos de datos y formas de inicializarlos: Tipo de dato Rango Ejemplo Byte Short Long Int Char Float Double Boolean String 8-bit con signo 16-bit con signo 64-bit con signo 32-bit con signo 16-bit sin signo 32 bit flotante con signo 64 bit flotante con signo true o false secuencia de caracteres 38 23 3434332 70 A’ 1.234 1.234 true “hola” • El API completo de Scala se puede consultar en http://www.scala-lang.org/ api/current/index.html jueves 7 de abril de 2011 Operadores • Aritméticos: + - = * % • Relacionales: < <= > >= != == • Lógicos: && || ! jueves 7 de abril de 2011 def para dar nombre a expresiones • def es una primitiva declarativa: le da un nombre a una expresión, pero no la evalua • La evaluación se realiza cuando se llame al identificador def t = 8 / 0 --> No da error t --> al evaluar t, error división por cero • En el caso de val o var la evaluación se realiza antes de hacer la asignación val t = 8 / 0 --> error jueves 7 de abril de 2011 ¿Cuándo se evalúan las variables en un def? • Las variables en una expresión definida por def se evaluan cuando se invoca al identificador definido: var x = 10 def t = x / 5 var x = 5 t -> 1 jueves 7 de abril de 2011 def para definir funciones • Sintaxis: def <nombre_funcion>(<parametro1:tipo1>,...):<tipo_resultado>={ <cuerpo de la función> } • Ejemplo: def max(x: Int, y: Int): Int = { if (x > y) x else y } max: (Int,Int)Int jueves 7 de abril de 2011 def para definir funciones • Las llaves son opcionales si el cuerpo tiene una sóla sentencia • El tipo de retorno es opcional si Scala puede inferirlo (no aplicable a funciones recursivas) scala> def max2(x: Int, y: Int) = if (x > y) x else y max2: (Int,Int)Int • La función se evalúa cuando se invoca: scala> max2(3, 5) res6: Int = 5 • Los ejemplos anteriores en los que se definían expresiones se pueden interpretar de esta manera: como una definición de una función de una única sentencia (la expresión) que se evalua cuando se invoca jueves 7 de abril de 2011 Una función que no devuelve nada • Por ahora todas las funciones que vamos a definir van a estar en el paradigma funcional, siempre devolverán algún valor • Sin embargo Scala es también procedural: scala> def hola() = println("Hola mundo") hola: ()Unit • La función hola no devuelve ningún valor; la clase Unit es similar al tipo void de Java jueves 7 de abril de 2011 Expresiones condicionales: if • if: condicional; cuidado con el fin de línea si no utilizamos llaves • Correcto: • Incorrecto: • Correcto: jueves 7 de abril de 2011 def abs(x: Double) = if (x >= 0) x else -x def abs(x: Double) = if (x >= 0) x else -x def abs(x: Double) = if (x >= 0) x else -x Expresiones if anidadas def entre(x: Double, x1: Double, x2: Double) = if (x < x1) false else if (x > x2) false else true jueves 7 de abril de 2011 Expresiones condicionales: match • Similar al cond de Scheme var myVar = 3; myVar match { case 1 => "Uno" case 2 => "Dos" case 3 => "Tres" case 4 => "Cuatro" } • El “default” se escribe como _ case _ => “otro caso” jueves 7 de abril de 2011 Funciones recursivas • El típico ejemplo de función recursiva: factorial en Scala def factorial(x: Long): Long = if (x == 0) 1 else x * factorial(x - 1) • Máximo común divisor recursivo: def gcd(x: Long, y:Long): Long = if (y == 0) x else gcd(y, x % y) jueves 7 de abril de 2011 Listas • La clase List de Scala permite definir listas • Al igual que Scheme son inmutables y tienen una estructura recursiva • A diferencia de Scheme son estrictamente tipeadas val val val val fruit = List("apples", "oranges", "pears") nums = List(1, 2, 3, 4) diag3 = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1)) empty = List() jueves 7 de abril de 2011 Operador cons • Funciona igual que en Scheme, produce una nueva lista añadiendo un nuevo elemento a su cabeza • Se define con 4 puntos :: val dosTres = List(2, 3) val unDosTres = 1 :: dosTres • La lista vacía se define con el identificador Nil val unoDosTres = 1 :: 2 :: 3 :: Nil val fruit = "apples" :: ("oranges" :: ("pears" :: Nil)) val nums = 1 :: (2 :: (3 :: (4 :: Nil))) val diag3 = (1::(0::(0::Nil))):: (0 :: (1 :: (0 :: Nil))) :: (0 :: (0 :: (1 :: Nil))) :: Nil val empty = Nil val nums = 1::2::3::4::Nil jueves 7 de abril de 2011 Operaciones básicas sobre listas • head: devuelve el primer elemento de la lista • tail: devuelve el resto de la lista • isEmpty: predicado que comprueba si la lista es vacía • ::: operador similar a append que concatena dos listas jueves 7 de abril de 2011 Funciones sobre listas • Con las operaciones previas se pueden definir funciones similares a las que vimos en Scheme • Inserción en una lista ordenada: jueves 7 de abril de 2011 Funciones sobre listas • Con las operaciones previas se pueden definir funciones similares a las que vimos en Scheme • Inserción en una lista ordenada: def insert(x: Int, lista: List[Int]) : List[Int] = if (lista.isEmpty) x :: Nil else if (x < lista.head) x :: lista else lista.head :: insert(x, lista.tail) jueves 7 de abril de 2011