manuales

20
Simbología de raptor Asignación. Es utilizado para cambiar el valor de la variable y hacer operaciones. Llamada. Se utiliza para hacer uso de agentes externos a los que se pueden manejar dentro de raptor. Un ejemplo particular del símbolo si se le agrega el nombre de clear_console cada que termine el proceso eliminará el historial del proceso anterior, de manera que no provoque confusiones entre cada proceso o se tenga almacenada información en determinado momento innecesaria. Entrada. Se utiliza para pedir un número o cadena, al mismo tiempo que el diagrama se encuentra en ejecución. Mientras que el juego de entrada se está ejecutando en un símbolo de asignación se le da valor a la variable que se introdujo en entrada. Salida. En este se escribe un número o texto en donde se explica cuál es el resultado de todo el proceso. Selección. Es utilizado para tomar decisiones, en donde se pone una ecuación lógica en la cual si la respuesta es si se realiza algo y si es no realiza todo lo contrario. LISTA DE INTRUCC IONES DE CADA COMANDO RAPTOR/ C#/ JAVA

Upload: elidetjc

Post on 26-Jul-2015

32 views

Category:

Software


3 download

TRANSCRIPT

Page 1: Manuales

Simbología de raptor

Asignación. Es utilizado para cambiar el valor

de la variable y hacer operaciones.

Llamada. Se utiliza para hacer uso de agentes

externos a los que se pueden manejar dentro de raptor. Un

ejemplo particular del símbolo si se le agrega el nombre de

clear_console cada que termine el proceso eliminará el

historial del proceso anterior, de manera que no provoque

confusiones entre cada proceso o se tenga almacenada

información en determinado momento innecesaria.

Entrada. Se utiliza para pedir un número o

cadena, al mismo tiempo que el diagrama se encuentra en

ejecución. Mientras que el juego de entrada se está

ejecutando en un símbolo de asignación se le da valor a la variable que se introdujo en

entrada.

Salida. En este se escribe un número o texto en donde se explica cuál es el resultado de

todo el proceso.

Selección. Es utilizado para tomar decisiones, en donde se pone una ecuación lógica en

la cual si la respuesta es si se realiza algo y si es no realiza todo lo contrario.

Ciclo. Es utilizado para repetir una secuencia de símbolos de manera que se detenga

hasta que determinada condición se cumpla. Funciona de forma que cuando el problema

llega a la parte inferior regresa a la parte superior a repetir el proceso hasta que la

condición se cumpla.

Interfaz del software[editar]

LISTA DE INTRUCCIONES DE

CADA COMAND

O RAPTOR/ C#/ JAVA

Page 2: Manuales

Al iniciar el programa te muestra dos pantalla: La pantalla más grande es en donde se va

realizando el proceso y en la pantallita chica cuando se tiene realizado el programa y se corre

nos va apareciendo el proceso ya realizado. Al realizar un diagrama de flujo siempre se debe

tener un inicio y un fin para demostrar cuando es que comienza el proceso y cuando termina,

estos son indicados con start y end respectivamente, los cuales siempre nos van a aparecer

en la pantalla. A diferencia de start y end que siempre están la pantalla el resto de los

comandos deben irse agregando según las necesidades de cada problema a resolver, se le

da doble clic al símbolo hasta que aparezca en rojo y después se da clic en la parte del

diagrama donde se agregara, y así se va haciendo hasta terminar el proceso.

Los cuatro botones negros y el lápiz que aparecen en la parte superior del programa sirven

para:

El primero es play: que sirve para que el programa corra.

El segundo es pausado: se utiliza cuando se desea detener el proceso para analizar cierta

parte del diagrama.

El tercero es stop que se utiliza para detener el proceso.

El cuarto se llama step to next shape que se utiliza para brincar una parte del proceso.

El lápiz se utiliza para agregar cierto escrito al diagrama.

Page 3: Manuales

Puede enviar argumentos al método Main definiendo el método en una de las siguientes maneras:C#

static int Main(string[] args)

C#

static void Main(string[] args)

 Nota

Para habilitar argumentos de la línea de comandos en el método Main de una aplicación de formularios Windows Forms, debe modificar manualmente la firma de Main en program.cs. El código generado por el diseñador de Windows Forms crea un elemento MainutilizarEnvironment.CommandLine o Environment.GetCommandLineArgs a fin de obtener acceso a los argumentos de la línea de comandos desde cualquier punto en una aplicación de consola o para Windows.

El parámetro del método Main es una matriz de tipo String que representa los argumentos de la línea de comandos. Normalmente se comprueba si existen argumentos mediante la propiedad Length, por ejemplo:C#

if (args.Length == 0){ System.Console.WriteLine("Please enter a numeric argument."); return 1;}

También se pueden convertir los argumentos de tipo string en tipos numéricos mediante la clase Convert el método Parse. Por ejemplo, la instrucción siguiente conviertestring en un número long mediante el método Parse:long num = Int64.Parse(args[0]);

También se puede utilizar el tipo long de C#, que equivale a Int64:long num = long.Parse(args[0]);

Igualmente, es posible usar el método ToInt64 de la clase Convert para lograr el mismo objetivo:long num = Convert.ToInt64(s);

Para obtener más información, vea Parse y Convert.

Comandos de

c#

Page 4: Manuales

EjemploEn el ejemplo siguiente se muestra cómo utilizar argumentos de la línea de comandos en una aplicación de consola. La aplicación toma un argumento en tiempo de ejecución, lo convierte en un número entero y calcula el valor factorial del número. Si no se proporciona ningún argumento, la aplicación emite un mensaje en el que se describe la forma de utilizarlo correctamente.Para compilar y ejecutar la aplicación desde un símbolo del sistema, siga estos pasos:

1. Pegue el código siguiente en cualquier editor de texto y, a continuación, guarde el archivo como archivo de texto con el nombre Factorial.cs.

C#

//Add a using directive for System if the directive isn't already present.

public class Functions{ public static long Factorial(int n) { // Test for invalid input if ((n < 0) || (n > 20)) { return -1; }

// Calculate the factorial iteratively rather than recursively: long tempResult = 1; for (int i = 1; i <= n; i++) { tempResult *= i; } return tempResult; }}

class MainClass{ static int Main(string[] args) { // Test if input arguments were supplied: if (args.Length == 0) { System.Console.WriteLine("Please enter a numeric argument."); System.Console.WriteLine("Usage: Factorial <num>"); return 1;

Page 5: Manuales

}

// Try to convert the input arguments to numbers. This will throw // an exception if the argument is not a number. // num = int.Parse(args[0]); int num; bool test = int.TryParse(args[0], out num); if (test == false) { System.Console.WriteLine("Please enter a numeric argument."); System.Console.WriteLine("Usage: Factorial <num>"); return 1; }

// Calculate factorial. long result = Functions.Factorial(num);

// Print result. if (result == -1) System.Console.WriteLine("Input must be >= 0 and <= 20."); else System.Console.WriteLine("The Factorial of {0} is {1}.", num, result);

return 0; }}// If 3 is entered on command line, the// output reads: The factorial of 3 is 6.

2. Desde la pantalla Iniciar o desde el menú Iniciar, abra una ventana Símbolo del sistema para desarrolladores de Visual Studio y después navegue hasta la carpeta que contiene el archivo que acaba de crear.

3. Especifique el comando siguiente para compilar la aplicación.csc Factorial.csSi la aplicación no tiene ningún error de compilación, se crea un archivo ejecutable denominado Factorial.exe.

4. Escriba el comando siguiente para calcular el factorial de 3:Factorial 3

5. El comando produce este resultado: The factorial of 3 is 6.

 Nota

Page 6: Manuales

Si ejecuta una aplicación en Visual Studio, puede especificar argumentos de la línea de comandos en Página Depuración, Diseñador de proyectos

Tutorial de Scala para programadores Java

     Por Michel Schinz y Philipp Haller. Traducción y arreglos Santiago Basulto.

IntroducciónEste documento provee una rápida introducción al lenguaje Scala como también a su compilador. Está pensado para personas que ya poseen cierta experiencia en programación y quieren una vista rápida de lo que pueden hacer con Scala. Se asume como un conocimiento básico de programación orientada a objetos, especialmente en Java.

Un primer ejemploComo primer ejemplo, usaremos el programa Hola mundo estándar. No es muy fascinante, pero de esta manera resulta fácil demostrar el uso de herramientas de Scala sin saber demasiado acerca del lenguaje. Veamos como luce:

1.object HolaMundo {

2. def main(args: Array[String]) {

3. println("¡Hola, mundo!")

4. }

5.}

La estructura de este programa debería ser familiar para programadores Java: consiste de un método llamado main que toma los argumentos de la línea de comando (un array de objetos String)

como parámetro; el cuerpo de este método consiste en una sola llamada al método predefinidoprintln con el saludo amistoso como argumento. El método main no retorna un valor

(se puede entender como un procedimiento). Por lo tanto, no es necesario que se declare un tipo retorno.Lo que es menos familiar a los programadores Java es la declaración de object que contiene al método main. Esa declaración introduce lo que es comúnmente conocido como objeto singleton,

que es una clase con una sola instancia. Por lo tanto, dicha construcción declara tanto una clase

COMANDOS DE

JAVA

Page 7: Manuales

llamada HolaMundo como una instancia de esa clase también llamada HolaMundo. Esta instancia

es creada bajo demanda, es decir, la primera vez que es utilizada.El lector astuto notará que el método main no es declarado como static. Esto es así porque los

miembros estáticos (métodos o campos) no existen en Scala. En vez de definir miembros estáticos, el programador de Scala declara estos miembros en un objeto singleton.

Compilando el ejemploPara compilar el ejemplo utilizaremos scalac, el compilador de Scala. scalac funciona como la

mayoría de los compiladores. Toma un archivo fuente como argumento, algunas opciones y produce uno o varios archivos objeto. Los archivos objeto que produce son archivos class de Java estándar.Si guardamos el programa anterior en un archivo llamado HolaMundo.scala, podemos compilarlo ejecutando el siguiente comando (el símbolo mayor > representa el prompt del shell y no debe ser

escrita):

1.> scalac HolaMundo.scala

Esto generará algunos archivos class en el directorio actual. Uno de ellos se llamaráHolaMundo.class y contiene una clase que puede ser directamente ejecutada utilizando el comando scala, como mostramos en la siguiente sección.

Ejecutando el ejemploUna vez compilado, un programa Scala puede ser ejecutado utilizando el comando scala. Su uso es muy similar al comando java utilizado para ejecutar programas Java, y acepta las mismas

opciones. El ejemplo de arriba puede ser ejecutado utilizando el siguiente comando, que produce la salida esperada:

1.> scala -classpath . HolaMundo

2.

3.¡Hola, mundo!

Interacción con JavaUna de las fortalezas de Scala es que hace muy fácil interactuar con código Java. Todas las clases del paquete java.lang son importadas por defecto, mientras otras necesitan ser importadas

explícitamente.Veamos un ejemplo que demuestra esto. Queremos obtener y formatear la fecha actual de acuerdo a convenciones utilizadas en un país específico, por ejemplo Francia.

Las librerías de clases de Java definen clases de utilería poderosas, como Date y DateFormat. Ya

que Scala interacciona fácilmente con Java, no es necesario implementar estas clases equivalentes en las librerías de Scala –podemos simplemente importar las clases de los correspondientes paquetes de Java:

1.import java.util.{Date, Locale}

2.import java.text.DateFormat

3.import java.text.DateFormat._

4.

5.object FrenchDate {

6. def main(args: Array[String]) {

7. val ahora = new Date

8. val df = getDateInstance(LONG, Locale.FRANCE)

Page 8: Manuales

9. println(df format ahora)

10. }

11. }

Las declaraciones de importación de Scala lucen muy similares a las de Java, sin embargo, las primeras son bastante más poderosas. Múltiples clases pueden ser importadas desde el mismo paquete al encerrarlas en llaves como se muestra en la primer línea. Otra diferencia es que podemos importar todos los nombres de un paquete o clase, utilizando el carácter guión bajo (_) en vez del asterisco (*). Eso es porque el asterisco es un identificador válido en Scala (quiere decir que por ejemplo podemos nombrar a un método *), como veremos más adelante.La declaración import en la tercer línea por lo tanto importa todos los miembros de la claseDateFormat. Esto hace que el método estático getDateInstance y el campo estático LONGsean directamente visibles.Dentro del método main primero creamos una instancia de la clase Date la cual por defecto

contiene la fecha actual. A continuación definimos un formateador de fechas utilizando el método estático getDateInstance que importamos previamente. Finalmente, imprimimos la fecha actual formateada de acuerdo a la instancia de DateFormat que fue “localizada”. Esta última línea

muestra una propiedad interesante de la sintaxis de Scala. Los métodos que toman un solo argumento pueden ser usados con una sintaxis de infijo Es decir, la expresión

1.df format ahora

es solamente otra manera más corta de escribir la expresión:

1.df.format(ahora)

Esto parece tener como un detalle sintáctico menor, pero tiene importantes consecuencias, una de ellas la exploraremos en la próxima sección.

Para concluir esta sección sobre la interacción con Java, es importante notar que es también posible heredar de clases Java e implementar interfaces Java directamente en Scala.

Todo es un objetoScala es un lenguaje puramente orientado a objetos en el sentido de que todo es un objeto, incluyendo números o funciones. Difiere de Java en este aspecto, ya que Java distingue tipos primitivos (como boolean e int) de tipos referenciales, y no nos permite manipular las funciones

como valores.

Los números son objetosYa que los números son objetos, estos también tienen métodos. De hecho, una expresión aritmética como la siguiente:

1.1 + 2 * 3 / x

Consiste exclusivamente de llamadas a métodos, porque es equivalente a la siguiente expresión, como vimos en la sección anterior:

1.(1).+(((2).*(3))./(x))

Esto también indica que +, *, etc. son identificadores válidos en Scala.

Los paréntesis alrededor de los números en la segunda versión son necesarios porque el analizador léxico de Scala usa la regla de “mayor coincidencia”. Por lo tanto partiría la siguiente expresión:

1.1.+(2)

Page 9: Manuales

En estas partes: 1., +, y 2. La razón que esta regla es elegida es porque 1. es una coincidencia válida y es mayor que 1, haciendo a este un Double en vez de un Int. Al escribir la expresión así:

1.(1).+(2)

previene que el 1 sea tomado como un Double.

Las funciones son objetosTal vez suene más sorprendente para los programadores Java, las funciones en Scala también son objetos. Por lo tanto es posible pasar funciones como argumentos, almacenarlas en variables, y retornarlas desde otras funciones. Esta habilidad de manipular funciones como valores es una de las valores fundamentales de un paradigma de programación muy interesante llamado programación funcional.Como un ejemplo muy simple de por qué puede ser útil usar funciones como valores consideremos una función temporizador (o timer, en inglés) cuyo propósito es realizar alguna acción cada un segundo. ¿Cómo pasamos al temporizador la acción a realizar? Bastante lógico, como una función. Este simple concepto de pasar funciones debería ser familiar para muchos programadores: es generalmente utilizado en código relacionado con Interfaces gráficas de usuario (GUIs) para registrar “retrollamadas” (call-back en inglés) que son invocadas cuando un evento ocurre.En el siguiente programa, la función del temporizador se llama unaVezPorSegundo y recibe una función call-back como argumento. El tipo de esta función es escrito de la siguiente manera: () => Unit y es el tipo de todas las funciones que no toman argumentos ni retornan valores (el tipo Unites similar a void en Java/C/C++). La función principal de este programa simplemente

invoca esta función temporizador con una call-back que imprime una sentencia en la terminal. En otras palabras, este programa imprime interminablemente la sentencia “El tiempo vuela como una flecha” cada segundo.

1.object Temporizador {

2. def unaVezPorSegundo(callback: () => Unit) {

3. while (true) { callback(); Thread sleep 1000 }

4. }

5. def tiempoVuela() {

6. println("El tiempo vuela como una flecha...")

7. }

8. def main(args: Array[String]) {

9. unaVezPorSegundo(tiempoVuela)

10. }

11. }

Nota: si nunca tuviste experiencias previas con programación funcional te recomiendo que te tomes unos segundos para analizar cuando se utilizan paréntesis y cuando no en los lugares donde aparece *callback*. Por ejemplo, dentro de la declaración de unaVezPorSegundo no aparece, ya que se trata de la función como un “valor”, a diferencia de cómo aparece dentro del método, ya que en ese caso se la está invocando (por eso los paréntesis). Note that in order to print the string, we used the predefined method println instead of using the one from System.out.

Funciones anónimasEl programa anterior es fácil de entender, pero puede ser refinado aún más. Primero que nada es interesante notar que la función tiempoVuela está definida solamente para ser pasada

Page 10: Manuales

posteriormente a la función unaVezPorSegundo. Tener que nombrar esa función, que es utilizada

solamente una vez parece un poco innecesario y sería bueno poder construirla justo cuando sea pasada a unaVezPorSegundo. Esto es posible en Scala utilizando funciones anónimas, que son

exactamente eso: funciones sin nombre. La versión revisada de nuestro temporizador utilizando una función anónima luce así:

1.object TemporizadorAnonimo {

2. def unaVezPorSegundo(callback: () => Unit) {

3. while (true) { callback(); Thread sleep 1000 }

4. }

5. def main(args: Array[String]) {

6. unaVezPorSegundo(

7. () => println("El tiempo vuela como una flecha...")

8. )

9. }

10. }

La presencia de una función anónima en este ejemplo es revelada por la flecha a la derecha => que separa los argumentos de la función del cuerpo de esta. En este ejemplo, la lista

de argumentos está vacía, como se ve por el par de paréntesis vacíos a la izquierda de la flecha. El cuerpo de la función es el mismo que en tiempoVuela del programa anterior.

ClasesComo hemos visto anteriormente, Scala es un lenguaje orientado a objetos, y como tal tiene el concepto de Clase (en realidad existen lenguajes orientados a objetos que no cuentan con el concepto de clases, pero Scala no es uno de ellos). Las clases en Scala son declaradas utilizando una sintaxis que es cercana a la de Java. Una diferencia importante es que las clases en Scala pueden tener parámetros. Ilustramos esto en el siguiente ejemplo, la definición de un número complejo:

1.class Complejo(real: Double, imaginaria: Double) {

2. def re() = real

3. def im() = imaginaria

4.}

Esta clase compleja toma dos argumentos, que son las partes real e imaginarias de un número complejo. Estos argumentos deben ser pasados cuando se crea una instancia de la claseComplejo, de la siguiente manera:

1.new Complejo(1.5, 2.3)

La clase contiene dos métodos llamados re e im, que proveen acceso a las dos partes del número.

Debe notarse que el tipo de retorno de estos dos métodos no está expresado explícitamente. Será inferido automáticamente por el compilador, que primero mira la parte derecha de estos métodos y puede deducir que ambos retornan un valor de tipo Double.

El compilador no es siempre capaz de inferir los tipos como lo hace aquí, y desafortunadamente no existe una regla simple para saber cuándo será y cuándo no. En la práctica, esto generalmente no es un problema ya que el compilador se queja cuando no es capaz de inferir un tipo que no fue explícitamente fijado. Como regla simple, los programadores de Scala novatos deberían tratar de omitir las declaraciones de tipos que parecen ser simples de deducir del contexto y ver si el compilador no lanza errores. Después de algún tiempo, el programador debería tener una buena idea de cuando omitir tipos y cuando explicitarlos.

Page 11: Manuales

Métodos sin argumentosUn pequeño problema de los métodos re e im es que para poder llamarlos es necesario agregar

un par de paréntesis vacíos después de sus nombres, como muestra el siguiente ejemplo:

1.object NumerosComplejos {

2. def main(args: Array[String]) {

3. val c = new Complejo(1.2, 3.4)

4. println("Parte imaginaria: " + c.im())

5. }

6.}

Sería mejor poder acceder las partes imaginarias y reales como si fueran campos, sin poner los paréntesis vacíos. Esto es perfectamente realizable en Scala, simplemente al definirlos comométodos sin argumentos. Tales métodos difieren de los métodos con cero o más argumentos en que no tienen paréntesis después de su nombre, tanto en la definición como en el uso. Nuestra clase Complejo puede ser reescrita así:

1.class Complejo(real: Double, imaginaria: Double) {

2. def re = real

3. def im = imaginaria

4.}

Herencia y sobreescrituraTodas las clases en Scala heredan de una superclase. Cuando ninguna superclase es especificada, como es el caso de Complejo se utiliza implícitamente scala.AnyRef.

Es posible sobreescribir métodos heredados de una superclase en Scala. Aunque es necesario explicitar específicamente que un método sobreescribe otro utilizando el modificador override, de manera de evitar sobreescrituras accidentales. Como ejemplo, nuestra clase Complejo puede ser aumentada con la redefinición del método toString heredado de Object.

1.class Complejo(real: Double, imaginaria: Double) {

2. def re = real

3. def im = imaginaria

4. override def toString() =

5. "" + re + (if (im < 0) "" else "+") + im + "i"

6.}

Clases Case y Reconocimiento de patronesUn tipo de estructura de datos que aparece seguido en programas es el Árbol. Por ejemplo, los intérpretes y compiladores usualmente representan los programas internamente como árboles; los documentos XML son árboles; y muchos otros tipos de contenedores están basados en árboles, como los árboles rojo y negro.

Ahora examinaremos cómo estos árboles son representados y manipulados en Scala mediante un pequeño programa que oficie de calculadora. El objetivo de este programa es manipular expresiones aritméticas simples compuestas de sumas de enteros y variables. Dos ejemplos de estas expresiones pueden ser: 1+2 y (x+x)+(7+y).

Page 12: Manuales

Primero tenemos que decidir una representación para tales expresiones. La más natural es un árbol, donde los nodos son las operaciones (la adición en este caso) y las hojas son valores (constantes o variables).

En Java, un árbol así sería representado utilizando una superclase abstracta para los árboles, y una subclase concreta por nodo u hoja. En un lenguaje de programación funcional uno utilizaría un tipo de dato algebraico para el mismo propósito. Scala provee el concepto de clases case que está en el medio de los dos conceptos anteriores. Aquí mostramos como pueden ser usadas para definir el tipo de los árboles en nuestro ejemplo:

1.abstract class Arbol

2.case class Sum(l: Arbol, r: Arbol) extends Arbol

3.case class Var(n: String) extends Arbol

4.case class Const(v: Int) extends Arbol

El hecho de que las clases Sum, Var y Const sean declaradas como clases case significa que

dififieren de las clases normales en varios aspectos: no es obligatorio utilizar la palabra clave new para crear instancias de estas

clases (es decir, se puede escribir Const(5) en lugar de new Const(5)), se crea automáticamente un “getter” (un método para obtener el valor) para los

parámetros utilizados en el constructor (por ejemplo es posible obtener el valor de v de una instancia c de la clase Const de la siguiente manera: c.v),

se proveen definiciones por defecto de los métodos equals y hashCode, que trabajan sobre la estructura de las instancias y no sobre su identidad,

se crea una definición por defecto del método toString que imprime el valor de una forma “tipo código) (ej: la expresión del árbol x+1 se imprimiría Sum(Var(x),Const(1))),

las instancias de estas clases pueden ser descompuestas mediante reconocimiento de patrones (pattern matching) como veremos más abajo.

Ahora que hemos definido el tipo de datos para representar nuestra expresión aritmética podemos empezar definiendo operaciones para manipularlas. Empezaremos con una función para evaluar una expresión en un entorno. El objetivo del entorno es darle valores a las variables. Por ejemplo, la expresión x+1 evaluada en un entorno que asocia el valor 5 a la variable x, escrito { x -> 5 }, da como resultado 6.

Por lo tanto tenemos que encontrar una manera de representar entornos. Podríamos por supuesto utilizar alguna estructura de datos asociativa como una tabla hash, pero podemos directamente utilizar funciones! Un entorno realmente no es nada más que una función la cual asocia valores a variables. El entorno { x -> 5 } mostrado anteriormente puede ser fácilmente escrito de la

siguiente manera en Scala:

1.{ case "x" => 5 }

Esta notación define una función la cual, dado un string "x" como argumento retorna el entero 5, y

falla con una excepción si no fuera así.Antes de escribir la función evaluadora, démosle un nombre al tipo de los entornos. Podríamos por supuesto simplemente utilizar String => Int para los entornos, pero simplifica el programa

introducir un nombre para este tipo, y hace que los futuros cambios sean más fáciles. Esto lo realizamos de la siguiente manera:

1.type Entorno = String => Int

De ahora en más, el tipo Entorno puede ser usado como un alias del tipo de funciones definidas de String a Int.

Page 13: Manuales

Ahora podemos dar la definición de la función evaluadora. Conceptualmente, es muy sencillo: el valor de una suma de dos expresiones es simplemente la suma de los valores de estas expresiones; el valor de una variable es obtenido directamente del entorno; y el valor de una constante es la constante en sí misma. Expresar esto en Scala no resulta para nada difícil:

1.def eval(a: Arbol, ent: Entorno): Int = a match {

2. case Sum(i, d) => eval(i, ent) + eval(d, env)

3. case Var(n) => ent(n)

4. case Const(v) => v

5.}

Esta función evaluadora función realizando un reconocimiento de patrones (pattern matching) en el árbol a. Intuitivamente, el significado de la definición de arriba debería estar claro:

1. Primero comprueba si el árbol tes una Sum, y si lo es, asocia el sub-arbol izquierdo a una nueva variable llamada i y el sub-arbol derecho a la variable r, y después procede con la evaluación de la expresión que sigue a la flecha (=>); esta expresión puede (y hace) uso de las variables asociadas por el patrón que aparece del lado izquierdo de la flecha.

2. si la primer comprobación (la de Sum) no prospera, es decir que el árbol no es una Sum, sigue de largo y comprueba si a es un Var; si lo es, asocia el nombre contenido en el nodo Var a la variable n y procede con la parte derecha de la expresión.

3. si la segunda comprobación también falla, resulta que a no es un Sum ni un Var, por lo tanto comprueba que sea un Const, y si lo es, asocia el valor contenido en el nodo Const a la variable vy procede con el lado derecho.

4. finalmente, si todos las comprobaciones fallan, una excepción es lanzada para dar cuenta el fallo de la expresión; esto puede pasar solo si existen más subclases de Arbol.

Hemos visto que la idea básica del reconocimiento de patrones es intentar coincidir un valor con una serie de patrones, y tan pronto como un patrón coincida, extraer y nombrar las varias partes del valor para finalmente evaluar algo de código que típicamente hace uso de esas partes nombradas.

Un programador con experiencia en orientación a objetos puede preguntarse por qué no definimoseval como un método de la clase Arbol y sus subclases. En realidad podríamos haberlo

hecho, ya que Scala permite la definición de métodos en clases case tal como en clases normales. Por lo tanto decidir en usar reconocimiento de patrones o métodos es una cuestión de gustos, pero también tiene grandes implicancias en cuanto a la extensibilidad:

cuando usamos métodos, es fácil añadir un nuevo tipo de nodo ya que esto puede ser realizado simplemente al definir una nueva subclase de Arbol; por otro lado, añadir una nueva operación para manipular el árbol es tedioso, ya que requiere la modificación en todas las subclases.

cuando utilizamos reconocimiento de patrones esta situación es inversa: agregar un nuevo tipo de nodo requiere la modificación de todas las funciones que hacen reconocimiento de patrones sobre el árbol, para tomar en cuenta un nuevo nodo; pero por otro lado agregar una nueva operación fácil, solamente definiendolo como una función independiente.

Para explorar un poco más esto de pattern matching definamos otra operación aritmética: derivación simbólica. El lector recordará las siguientes reglas sobre esta operación:

Page 14: Manuales

1. la derivada de una suma es la suma de las derivadas,2. la derivada de una variable v es uno (1) si v es la variable relativa a la cual la

derivada toma lugar, y cero (0)de otra manera,3. la derivada de una constante es cero (0).

Estas reglas pueden ser traducidas casi literalmente en código Sclaa, para obtener la siguiente definición.

1.def derivada(a: Arbol, v: String): Arbol = a match {

2. case Sum(l, r) => Sum(derivada(l, v), derivada(r, v))

3. case Var(n) if (v == n) => Const(1)

4. case _ => Const(0)

5.}

Esta función introduce dos nuevos conceptos relacionados al pattern matching. Primero que nada la expresión case para variables tienen una guarda, una expresión siguiendo la palabra clave if.

Esta guarda previene que el patrón concuerde al menos que la expresión sea verdadera. Aquí es usada para asegurarse que retornamos la constante 1 solo si el nombre de la variable siendo derivada es el mismo que la variable derivada v. El segundo concepto nuevo usado aquí es elcomodín, escrito con el guión bajo _, que coincide con cualquier valor que aparezca, sin darle un

nombre.No hemos explorado el completo poder del pattern matching aún, pero nos detendremos aquí para mantener este documento corto. Todavía nos queda pendiente ver cómo funcionan las dos funciones de arriba en un ejemplo real. Para ese propósito, escribamos una función main simple que realice algunas operaciones sobre la expresión (x+x)+(7+y): primero computa su valor en el entorno { x -> 5, y -> 7 } y después computa su derivada con respecto a x y después a y.

1.def main(args: Array[String]) {

2. val exp: Arbol = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y")))

3. val ent: Entonrno = { case "x" => 5 case "y" => 7 }

4. println("Expresión: " + exp)

5. println("Evaluación con x=5, y=7: " + eval(exp, ent))

6. println("Derivada con respecto a x:\n " + derivada(exp, "x"))

7. println("Derivada con respecto a y:\n " + derivada(exp, "y"))

8.}

Al ejecutar este programa obtenemos el siguiente resultado:

1.Expresión: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y)))

2.Evaluación con x=5, y=7: 24

3.Derivada con respecto a x:

4. Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0)))

5.Derivada con respecto a y:

6. Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1)))

Al examinar la salida vemos que el resultado de la derivada debería ser simplificado antes de ser presentado al usuario. Definir una función de simplificación básica utilizando reconocimiento de patrones es un problema interesante (y, por no decir complejo, que necesita una solución astuta), lo dejamos para un ejercicio para el lector.

Traits

Page 15: Manuales

Nota: La palabra Trait(/treɪt/, pronunciado Treit) puede ser traducida literalmente como “Rasgo”. De todas maneras decido utilizar la notación original por ser un concepto muy arraigado a ScalaAparte de poder heredar código de una super clase, una clase en Scala puede también importar código de uno o varios traits.Tal vez la forma más fácil para un programador Java de entender qué son los traits es verlos como interfaces que también pueden contener código. En Scala, cuando una clase hereda de un trait, implementa la interface de ese trait, y hereda todo el código contenido en el trait.

Para ver la utilidad de los traits, veamos un ejemplo clásico: objetos ordenados. Generalmente es útil tener la posibilidad de comparar objetos de una clase dada entre ellos, por ejemplo, para ordenarlos. En Java, los objetos que son comparables implementan la interfaz Comparable. En

Scala, podemos hacer algo un poco mejor que en Java al definir un trait equivalente Comparableque invocará a Ord.

Cuando comparamos objetos podemos utilizar seis predicados distintos: menor, menor o igual, igual, distinto, mayor o igual y mayor. De todas maneras, definir todos estos es fastidioso, especialmente que cuatro de estos pueden ser expresados en base a los otros dos. Esto es, dados los predicados “igual” y “menor” (por ejemplo), uno puede expresar los otros. En Scala, todas estas observaciones pueden ser fácilmente capturadas mediante la siguiente declaración de un Trait:

1.trait Ord {

2. def < (that: Any): Boolean

3. def <=(that: Any): Boolean = (this < that) || (this == that)

4. def > (that: Any): Boolean = !(this <= that)

5. def >=(that: Any): Boolean = !(this < that)

6.}

Esta definición crea un nuevo tipo llamado Ord el cual juega el mismo rol que la interfazComparable, como también provee implementaciones de tres predicados en términos de un

cuarto, abstracto. Los predicados para igualidad y su inverso (distinto, no igual) no aparecen aquí ya que por defecto están presenten en todos los objetos.El tipo Any el cual es usado arriba es el supertipo de todos los otros tipos en Scala. Puede ser visto como una versión más general del tipo Object en Java, ya que Any también es supertipo deInt, Float, etc. cosa que no se cumple en Java (int por ejemplo es un tipo primitivo).

Para hacer a un objeto de la clase comparable es suficiente definir los predicados que comprueban la igualdad y la inferioridad y mezclar la clase Ord de arriba. Como un ejemplo, definamos una clase Fecha que representa fechas en el calendario gregoriano.

1.class Fecha(d: Int, m: Int, a: Int) extends Ord {

2. def anno = a

3. def mes = m

4. def dia = d

5. override def toString(): String = anno + "-" + mes + "-" + dia

La parte importante aquí es la declaración extends Ord la cual sigue al nombre de la clase y los parámetros. Declara que la clase Fecha hereda del trait Ord.Después redefinimos el método equals, heredado de Object, para comparar correctamente fechas mediante sus campos individuales. La implementación por defecto de equals no es

utilizable, porque como en Java, compara los objetos físicamente. Por lo tanto llegamos a esto:

1.override def equals(that: Any): Boolean =

Page 16: Manuales

2. that.isInstanceOf[Fecha] && {

3. val o = that.asInstanceOf[Fecha]

4. o.dia== dia && o.mes == mes && o.anno== anno

5. }

Este método utiliza el método predefinido isInstanceOf (“es instancia de”) y asInstanceOf(“como instancia de”). El primero isInstanceOf se corresponde con el operador java instanceOfy retorna true si y solo si el objeto en el cual es aplicado es una instancia del tipo dado. El segundo, asInstanceOf, corresponde al operador de casteo en Java: si el objeto es una instancia

de un tipo dado, esta es vista como tal, de otra manera se lanza una excepciónClassCastException.

Finalmente el último método para definir es el predicado que comprueba la inferioridad. Este hace uso de otro método predefinido, error que lanza una excepción con el mensaje de error provisto.

1.def <(that: Any): Boolean = {

2. if (!that.isInstanceOf[Fecha])

3. error("no se puede comparar" + that + " y una fecha")

4.

5. val o = that.asInstanceOf[Fecha]

6. (anno < o.anno) ||

7. (anno== o.anno && (mes < o.mes ||

8. (mes == o.mes && dia < o.dia)))

9.}

Esto completa la definición de la clase Fecha. Las instancias de esta clase pueden ser vistas tanto

como fechas o como objetos comparables. Además, todas ellas definen los seis predicados de comparación mencionados arriba: equals y < porque aparecen directamente en la definición de la clase Fecha y los otros porque son heredados del trait Ord.

Los traits son útiles en muchas otras más situaciones que las aquí mostrada, pero discutir sus aplicaciones está fuera del alcance de este documento.

Tipos GenéricosNota: El diseñador de los tipos genéricos en Java fue nada más ni nada menos que Martin Odersky, el diseñador de Scala.La última característica de Scala que exploraremos en este tutorial es la de los tipos genéricos. Los programadores de Java deben estar bien al tanto de los problemas que genera la falta de genéricos en su lenguaje, lo cual es solucionado en Java 1.5.

Los tipos genéricos proveen al programador la habilidad de escribir código parametrizado por tipos. Por ejemplo, escribir una librería para listas enlazadas se enfrenta al problema de decidir qué tipo darle a los elementos de la lista. Ya que esta lista está pensada para ser usada en diferentes contextos, no es posible decidir que el tipo de elementos sea, digamos, Int. Esto sería

completamente arbitrario y muy restrictivo.Los programadores Java cuentan como último recurso con Object, que es el supertipo de todos

los objetos. Esta solución de todas maneras está lejos de ser ideal, ya que no funciona con tipos primitivos (int, long, float, etc.) e implica que el programador tenga que realizar muchos casteos

de tipos en su programa.

Page 17: Manuales

Scala hace posible definir clases genéricas (y métodos) para resolver este problema. Examinemos esto con un ejemplo del contenedor más simple posible: una referencia, que puede estar tanto vacía como apuntar a un objeto de algún tipo.

1.class Referencia[T] {

2. private var contenido: T = _

3. def set(valor: T) { contenido = valor }

4. def get: T = contenido

5.}

La clase Referencia es parametrizada por un tipo llamado T, que es el tipo de sus elementos. Este tipo es usado en el cuerpo de la clase como el tipo de la variable contenido, el argumento del método set y el tipo de retorno del método get.

El ejemplo anterior introduce a las variables en Scala, que no deberían requerir mayor explicación. Es interesante notar que el valor inicial dado a la variable contenido es _, que representa un valor por defecto. Este valor por defecto es 0 para tipos numéricos, false para tipos Boolean, () para el tipo Unit y null para el resto de los objetos.Para utilizar esta clase Referencia, uno necesita especificar qué tipo utilizar por el parámetro T, es

decir, el tipo del elemento contenido por la referencia. Por ejemplo, para crear y utilizar una referencia que contenga un entero, podríamos escribir lo siguiente:

1.object ReferenciaEntero {

2. def main(args: Array[String]) {

3. val ref = new Referencia[Int]

4. ref.set(13)

5. println("La referncia tiene la mitad de " + (ref.get * 2))

6. }

7.}

Como puede verse en el ejemplo, no es necesario castear el valor retornado por el método getantes de usarlo como un entero. Tampoco es posible almacenar otra cosa que no sea

un entero en esa referencia en particular, ya que fue declarada como contenedora de un entero.