Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anterior Revisión previa Próxima revisión | Revisión previa | ||
|
functional:scala [2013/04/02 20:30] alfred [Uso] |
functional:scala [2020/05/09 09:25] (actual) |
||
|---|---|---|---|
| Línea 15: | Línea 15: | ||
| * ''console'', empieza el REPL (Scala interprete), para salir de él ''ctrl+d''. | * ''console'', empieza el REPL (Scala interprete), para salir de él ''ctrl+d''. | ||
| * ''compile'', compila el código en ese directorio. | * ''compile'', compila el código en ese directorio. | ||
| + | * ''run'', ejecutará el código que hay en el directorio del proyecto si este tiene un método ''main''. | ||
| Línea 27: | Línea 28: | ||
| } | } | ||
| </code> | </code> | ||
| + | * ''ctrl+shift+f'' formatea el código. | ||
| ===== Conceptos ===== | ===== Conceptos ===== | ||
| + | ==== Conceptos básicos ==== | ||
| + | |||
| + | === Vocabulario === | ||
| + | |||
| + | * **Clases**, el concepto es parecido al de la POO. Contienen propiedades y métodos, como en Java, pueden inicializarse a partir del ''new'' y de un constructor y pueden haber diversas instancias (u objetos) de la misma clase. | ||
| + | * **Traits**, son parecidas a las interfaces de la POO aunque también pueden contener implementaciones de métodos o definiciones de campos. | ||
| + | * **Objetos**, son como clases en POO pero la diferencia es que únicamente existe una instancia. | ||
| + | * **Paquetes**, Haciendo ''package foo.bar'' al principio de un fichero de código diremos que ese fichero pertenece al paquete ''foo.bar'' al cual, para importarlo, será necesario hacer ''import foo.bar._'' o haciendo ''foo.bar.MyClass'' si quiere importar una clase concreta o ''import.bar.baz._'' si queremos importar el objeto ''baz''. | ||
| + | |||
| + | === El main... === | ||
| + | |||
| + | En Scala hay dos formas de indicar un punto de entrada a un programa, aunque siempre será a partir de un objeto... \\ | ||
| + | Puede hacerse extendiendo el objeto de ''App''... | ||
| + | <code scala> | ||
| + | object HelloWorld extends App { | ||
| + | println("Hello, World!") | ||
| + | } | ||
| + | </code> | ||
| + | ... O añadiendo a un objeto el método ''def main(args: Array[String])'': | ||
| + | <code scala> | ||
| + | object HelloWorld { | ||
| + | def main(args: Array[String]) { | ||
| + | println("Hello, World!") | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | |||
| + | === Sintaxis de código === | ||
| + | |||
| + | Definir un valor llamado ''radious'': | ||
| + | <code scala> | ||
| + | def radious = 10 | ||
| + | </code> | ||
| + | Definir una expresión: | ||
| + | <code scala> | ||
| + | def sumOfSquares(x: Double, y: Double) = square(x) + square(y) | ||
| + | </code> | ||
| + | Definir el tipo de retorno de una expresión: | ||
| + | <code scala> | ||
| + | def sumOfSquares(x: Double, y: Double): Double = ... | ||
| + | </code> | ||
| + | Para evaluar una exprisión por nombre, en vez de por valor se usa ''<nowiki>=></nowiki>'': | ||
| + | <code scala> | ||
| + | def constOne(x:Int, y: => Int) = 1 | ||
| + | </code> | ||
| + | También puedes usar expresiones ''if-else'' de la siguiente forma: | ||
| + | <code scala> | ||
| + | def abs(x:Int) = if (x >= 0) x else -x | ||
| + | </code> | ||
| + | Tabla de operadores booleanos: | ||
| + | <code> | ||
| + | true && e --> e | ||
| + | false && e --> false | ||
| + | true || e --> true | ||
| + | false || e --> e | ||
| + | </code> | ||
| + | ''def'' significa "por nombre", ''val'' por valor. Por nombre se genera al evaluar esa expresión, por valor cuando se utiliza: | ||
| + | <code scala> | ||
| + | def loop: Boolean = loop | ||
| + | def x = loop // would die when it is used | ||
| + | val x = loop // would die here | ||
| + | </code> | ||
| + | |||
| + | Podemos hacer expresiones multilínea usando ''|'': | ||
| + | <code scala> | ||
| + | def and(x:Boolean, y:Boolean) = | ||
| + | | if (x) y else false | ||
| + | </code> | ||
| + | Otra opción para hacer expreiones multilínea es usando paréntesis: | ||
| + | <code scala> | ||
| + | (someLongExpression1 | ||
| + | + someLongExpression2) | ||
| + | </code> | ||
| + | O, una última opción de expreiones multilínea es el uso de operadores: | ||
| + | <code scala> | ||
| + | someLongExpression1 + | ||
| + | someLongExpression2 | ||
| + | </code> | ||
| + | |||
| + | Los bloques se delimitan por ''{ ... }'' y las definiciones existentes dentro de un bloque son visibles dentro de este, además ocultan las definiciones de fuera. \\ | ||
| + | |||
| + | Semicolons are optional in most cases. We can put several expression in one line: | ||
| + | El punto y coma es opcional en muchos casos, aún así nos permite para añadir más expresiones en una sola línea: | ||
| + | <code scala> | ||
| + | val y = 1; x + x | ||
| + | </code> | ||
| + | |||
| + | === Listas === | ||
| + | |||
| + | Un objeto List contiene los siguientes métodos: | ||
| + | * ''.isEmpty'': Indica si la lista está vacía. | ||
| + | * ''.head'': Devuelve el primer elemento. | ||
| + | * ''.tail'': Devuelve el resto de elementos distintos al primero. | ||
| + | |||
| + | <code scala> | ||
| + | def sum(xs: List[Int]): Int = { | ||
| + | if (xs.isEmpty) 0 | ||
| + | else xs.head + sum(xs.tail) | ||
| + | } | ||
| + | |||
| + | def max(xs: List[Int]): Int = { | ||
| + | if (xs.isEmpty) throw new java.util.NoSuchElementException | ||
| + | if (!xs.tail.isEmpty) { | ||
| + | val m = max(xs.tail) | ||
| + | if (xs.head > m) xs.head | ||
| + | else m | ||
| + | } else xs.head | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Excepciones === | ||
| + | <code scala> | ||
| + | if (xs.isEmpty) throw new java.util.NoSuchElementException | ||
| + | </code> | ||
| + | ==== Montar tests ==== | ||
| + | * {{:functional:scala:test_jars.tar.gz|Jars para tests}} | ||
| ==== Funciones y evaluación ==== | ==== Funciones y evaluación ==== | ||
| + | Sintaxis de definición: | ||
| + | <code> | ||
| + | def nombre (par_name : tipo) : tipo_return = { ... } | ||
| + | </code> | ||
| + | Para indicar en Scala que un parámetro (en este caso el segundo) se evaluará por nombre, haremos: | ||
| + | <code> | ||
| + | def first (x: Double, y: => Double) = x | ||
| + | </code> | ||
| + | |||
| + | === Diferencia entre evaluación por nombre y por valor === | ||
| + | Teniendo... | ||
| + | <code scala> | ||
| + | square (x: Double) : Double = x * x | ||
| + | sumOfSquares (x: Double, y: Double) : Double = square(x) + square(y) | ||
| + | </code> | ||
| + | Por valor cada argumento se evalua una vez: | ||
| + | <code> | ||
| + | sumOfSquares(3, 2+2) | ||
| + | sumOfSquares(3, 4) | ||
| + | square(3) + square(4) | ||
| + | 3 * 3 + square(4) | ||
| + | 9 + square(4) | ||
| + | 9 + 4 * 4 | ||
| + | 9 + 16 | ||
| + | 25 | ||
| + | </code> | ||
| + | |||
| + | Por nombre cada argumento se evalua cuando es llamado: | ||
| + | <code> | ||
| + | sumOfSquares(3, 2+2) | ||
| + | square(3) + square(2+2) | ||
| + | 3 * 3 + square(2+2) | ||
| + | 9 + square(2+2) | ||
| + | 9 + (2+2) * (2+2) | ||
| + | 9 + 4 * (2+2) | ||
| + | 9 + 4 * 4 | ||
| + | 25 | ||
| + | </code> | ||
| + | |||
| + | Si tuviesemos la siguientes expresiones | ||
| + | <code> | ||
| + | def loop () = loop | ||
| + | def first (x: Double, y: Double) = x | ||
| + | first(x, loop) | ||
| + | </code> | ||
| + | Si evaluamos la tercera por nombre terminaría pero si la evaluamos por valor cae en bucle infinito. \\ | ||
| + | Cuando definimos usando ''val'' lo hacemos haciéndolo por valor. Cuando lo hacemos usando ''def'' lo hacemos por nombre: | ||
| + | <code> | ||
| + | def loop () : Boolean = loop | ||
| + | def x = loop | ||
| + | val x = loop // bucle infinito | ||
| + | </code> | ||
| + | |||
| + | ==== Notas ==== | ||
| + | * Cuando tenemos una función recursiva necesitamos indicar su valor devuelto. | ||