SlideShare ist ein Scribd-Unternehmen logo
1 von 17
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]
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.
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 Wind
firma de Main en program.cs. El código generado por el diseñador de Windows Forms crea un elemento Ma
utilizarEnvironment.CommandLine o Environment.GetCommandLineArgs a fin de obtener acceso a los argu
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.
Ejemplo
En 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;
}
// 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.cs
Si 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
Si ejecuta una aplicación en Visual Studio, puede especificar argumentos de la línea de comandos en Página
Tutorial de Scala para programadores
Java
Por Michel Schinz y Philipp Haller. Traducción y arreglos Santiago Basulto.
Introducción
Este 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 ejemplo
Como 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
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 ejemplo
Para 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 ejemplo
Una 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 Java
Una 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)
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 objeto
Scala 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 objetos
Ya 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)
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 objetos
Tal 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ónimas
El 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
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.
Clases
Como 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.
Métodos sin argumentos
Un 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 sobreescritura
Todas 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 patrones
Un 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).
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 serdescompuestas 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.
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-arbolizquierdo 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 pasarsolo 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:
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
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 Scala
Aparte 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 =
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éricos
Nota: 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.
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.

Weitere ähnliche Inhalte

Was ist angesagt?

Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2camnav
 
Programación Orientada a Objetos - Unidad 5 Excepciones
Programación Orientada a Objetos - Unidad 5 ExcepcionesProgramación Orientada a Objetos - Unidad 5 Excepciones
Programación Orientada a Objetos - Unidad 5 ExcepcionesJosé Antonio Sandoval Acosta
 
Kit de supervivencia para Java 8 : como prepararse para Java 9
Kit de supervivencia para Java 8 :  como prepararse para Java 9Kit de supervivencia para Java 8 :  como prepararse para Java 9
Kit de supervivencia para Java 8 : como prepararse para Java 9Eudris Cabrera
 
Funciones en visual basic
Funciones en visual basicFunciones en visual basic
Funciones en visual basicDanny Mendoza
 
lenguaje pascal
lenguaje pascallenguaje pascal
lenguaje pascalotaru
 
Manejo de excepciones en vb
Manejo de excepciones en vbManejo de excepciones en vb
Manejo de excepciones en vbgerardd98
 
Fundamentos de Programación - Unidad II: Introducción a la Programación
Fundamentos de Programación - Unidad II: Introducción a la ProgramaciónFundamentos de Programación - Unidad II: Introducción a la Programación
Fundamentos de Programación - Unidad II: Introducción a la ProgramaciónJosé Antonio Sandoval Acosta
 
Java - Sintaxis Básica 2015
Java - Sintaxis Básica 2015Java - Sintaxis Básica 2015
Java - Sintaxis Básica 2015Renny Batista
 
Presentación sobre Algoritmos!!:)
Presentación sobre Algoritmos!!:)Presentación sobre Algoritmos!!:)
Presentación sobre Algoritmos!!:)MichellepRodriguez
 
Recopilando informacion
Recopilando informacionRecopilando informacion
Recopilando informacionJuarez Jose
 

Was ist angesagt? (19)

Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2Lenguajesdeprogramacion c nivel1-unidad2
Lenguajesdeprogramacion c nivel1-unidad2
 
Comandos c#
Comandos c#Comandos c#
Comandos c#
 
Programación Orientada a Objetos - Unidad 5 Excepciones
Programación Orientada a Objetos - Unidad 5 ExcepcionesProgramación Orientada a Objetos - Unidad 5 Excepciones
Programación Orientada a Objetos - Unidad 5 Excepciones
 
Kit de supervivencia para Java 8 : como prepararse para Java 9
Kit de supervivencia para Java 8 :  como prepararse para Java 9Kit de supervivencia para Java 8 :  como prepararse para Java 9
Kit de supervivencia para Java 8 : como prepararse para Java 9
 
Procedimientos en visual basic
Procedimientos en visual basicProcedimientos en visual basic
Procedimientos en visual basic
 
Funciones en visual basic
Funciones en visual basicFunciones en visual basic
Funciones en visual basic
 
Principios programacion
Principios programacionPrincipios programacion
Principios programacion
 
Funciones
FuncionesFunciones
Funciones
 
lenguaje pascal
lenguaje pascallenguaje pascal
lenguaje pascal
 
Logica subprogramas
Logica subprogramasLogica subprogramas
Logica subprogramas
 
Procedures funciones
Procedures funcionesProcedures funciones
Procedures funciones
 
Manejo de excepciones en vb
Manejo de excepciones en vbManejo de excepciones en vb
Manejo de excepciones en vb
 
Fundamentos de Programación - Unidad II: Introducción a la Programación
Fundamentos de Programación - Unidad II: Introducción a la ProgramaciónFundamentos de Programación - Unidad II: Introducción a la Programación
Fundamentos de Programación - Unidad II: Introducción a la Programación
 
Lenguaje de pascal
Lenguaje de pascalLenguaje de pascal
Lenguaje de pascal
 
Programacion
ProgramacionProgramacion
Programacion
 
Java - Sintaxis Básica 2015
Java - Sintaxis Básica 2015Java - Sintaxis Básica 2015
Java - Sintaxis Básica 2015
 
Presentación sobre Algoritmos!!:)
Presentación sobre Algoritmos!!:)Presentación sobre Algoritmos!!:)
Presentación sobre Algoritmos!!:)
 
Metodos en java
Metodos en javaMetodos en java
Metodos en java
 
Recopilando informacion
Recopilando informacionRecopilando informacion
Recopilando informacion
 

Ähnlich wie Manuales ...

Ähnlich wie Manuales ... (20)

Conociendo los Lenguajes de Programacion
Conociendo los Lenguajes de ProgramacionConociendo los Lenguajes de Programacion
Conociendo los Lenguajes de Programacion
 
Java 8
Java 8Java 8
Java 8
 
Introduccion a C++
Introduccion a C++Introduccion a C++
Introduccion a C++
 
Trabajo tutorial de visual C++
Trabajo tutorial de visual C++Trabajo tutorial de visual C++
Trabajo tutorial de visual C++
 
Javaintroduccion 2010
Javaintroduccion 2010Javaintroduccion 2010
Javaintroduccion 2010
 
Turbo Pascal 7
Turbo Pascal 7Turbo Pascal 7
Turbo Pascal 7
 
Java básico
Java  básicoJava  básico
Java básico
 
comandos
comandoscomandos
comandos
 
taller de tecnologia .pdf
taller de tecnologia .pdftaller de tecnologia .pdf
taller de tecnologia .pdf
 
taller de tecnologia .pdf
taller de tecnologia .pdftaller de tecnologia .pdf
taller de tecnologia .pdf
 
Java 8 - Nuevas características
Java 8 - Nuevas característicasJava 8 - Nuevas características
Java 8 - Nuevas características
 
sentenciareturnymetodos
sentenciareturnymetodossentenciareturnymetodos
sentenciareturnymetodos
 
Guía Práctica de Diagramas de Flujo
Guía Práctica de Diagramas de FlujoGuía Práctica de Diagramas de Flujo
Guía Práctica de Diagramas de Flujo
 
Algoritmos y lenjuage de programacion
Algoritmos y  lenjuage de programacionAlgoritmos y  lenjuage de programacion
Algoritmos y lenjuage de programacion
 
algoritmo
algoritmoalgoritmo
algoritmo
 
Funcionesclase1
Funcionesclase1Funcionesclase1
Funcionesclase1
 
Introducción a Python
Introducción a PythonIntroducción a Python
Introducción a Python
 
Datos de entrada_desde_el_teclado java
Datos de entrada_desde_el_teclado javaDatos de entrada_desde_el_teclado java
Datos de entrada_desde_el_teclado java
 
Semana 1 - Principios de Algoritmos.pdf
Semana 1 - Principios de Algoritmos.pdfSemana 1 - Principios de Algoritmos.pdf
Semana 1 - Principios de Algoritmos.pdf
 
Jlex Cup
Jlex CupJlex Cup
Jlex Cup
 

Mehr von elidetjc

Programa de eclipse
Programa de eclipsePrograma de eclipse
Programa de eclipseelidetjc
 
Programa 2
Programa 2 Programa 2
Programa 2 elidetjc
 
Programa 1
Programa 1Programa 1
Programa 1elidetjc
 
Enmuladores
EnmuladoresEnmuladores
Enmuladoreselidetjc
 
Preguntas 60
Preguntas 60Preguntas 60
Preguntas 60elidetjc
 
Examen de diagnostico_de_moviles
Examen de diagnostico_de_movilesExamen de diagnostico_de_moviles
Examen de diagnostico_de_movileselidetjc
 
Joselyn lizbeth balderas_aguirre
Joselyn lizbeth balderas_aguirreJoselyn lizbeth balderas_aguirre
Joselyn lizbeth balderas_aguirreelidetjc
 
Examen de diagnostico_de_moviles_2_22
Examen de diagnostico_de_moviles_2_22Examen de diagnostico_de_moviles_2_22
Examen de diagnostico_de_moviles_2_22elidetjc
 
Examen de diagnostico_de_moviles2
Examen de diagnostico_de_moviles2Examen de diagnostico_de_moviles2
Examen de diagnostico_de_moviles2elidetjc
 
Preguntas602
Preguntas602Preguntas602
Preguntas602elidetjc
 
Presentacion de power_de_emuladores2
Presentacion de power_de_emuladores2Presentacion de power_de_emuladores2
Presentacion de power_de_emuladores2elidetjc
 
sooooooooooooooooooffff
sooooooooooooooooooffffsooooooooooooooooooffff
sooooooooooooooooooffffelidetjc
 
Cuestionario prgts
Cuestionario prgtsCuestionario prgts
Cuestionario prgtselidetjc
 

Mehr von elidetjc (20)

Programa de eclipse
Programa de eclipsePrograma de eclipse
Programa de eclipse
 
Codigos
CodigosCodigos
Codigos
 
Programa 2
Programa 2 Programa 2
Programa 2
 
Programa 1
Programa 1Programa 1
Programa 1
 
Tutorial
TutorialTutorial
Tutorial
 
Enmuladores
EnmuladoresEnmuladores
Enmuladores
 
Preguntas 60
Preguntas 60Preguntas 60
Preguntas 60
 
Examen de diagnostico_de_moviles
Examen de diagnostico_de_movilesExamen de diagnostico_de_moviles
Examen de diagnostico_de_moviles
 
Joselyn lizbeth balderas_aguirre
Joselyn lizbeth balderas_aguirreJoselyn lizbeth balderas_aguirre
Joselyn lizbeth balderas_aguirre
 
Examen de diagnostico_de_moviles_2_22
Examen de diagnostico_de_moviles_2_22Examen de diagnostico_de_moviles_2_22
Examen de diagnostico_de_moviles_2_22
 
Examen de diagnostico_de_moviles2
Examen de diagnostico_de_moviles2Examen de diagnostico_de_moviles2
Examen de diagnostico_de_moviles2
 
Preguntas602
Preguntas602Preguntas602
Preguntas602
 
Presentacion de power_de_emuladores2
Presentacion de power_de_emuladores2Presentacion de power_de_emuladores2
Presentacion de power_de_emuladores2
 
Ejem8
Ejem8Ejem8
Ejem8
 
sooooooooooooooooooffff
sooooooooooooooooooffffsooooooooooooooooooffff
sooooooooooooooooooffff
 
software
softwaresoftware
software
 
Ejem5
Ejem5Ejem5
Ejem5
 
Loo toy
Loo toyLoo toy
Loo toy
 
Cuestionario prgts
Cuestionario prgtsCuestionario prgts
Cuestionario prgts
 
Ejemplo 4
Ejemplo 4Ejemplo 4
Ejemplo 4
 

Manuales ...

  • 1. 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]
  • 2. 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.
  • 3. 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 Wind firma de Main en program.cs. El código generado por el diseñador de Windows Forms crea un elemento Ma utilizarEnvironment.CommandLine o Environment.GetCommandLineArgs a fin de obtener acceso a los argu 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);
  • 4. Para obtener más información, vea Parse y Convert. Ejemplo En 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;
  • 5. } // 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.cs Si 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
  • 6. Si ejecuta una aplicación en Visual Studio, puede especificar argumentos de la línea de comandos en Página Tutorial de Scala para programadores Java Por Michel Schinz y Philipp Haller. Traducción y arreglos Santiago Basulto. Introducción Este 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 ejemplo Como 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
  • 7. 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 ejemplo Para 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 ejemplo Una 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 Java Una 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)
  • 8. 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 objeto Scala 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 objetos Ya 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)
  • 9. 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 objetos Tal 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ónimas El 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 posteriormente a la función unaVezPorSegundo. Tener que nombrar esa función, que es utilizada
  • 10. 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. Clases Como 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.
  • 11. Métodos sin argumentos Un 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 sobreescritura Todas 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 patrones Un 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).
  • 12. 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 serdescompuestas 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. 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
  • 13. 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-arbolizquierdo 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 pasarsolo 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: 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.
  • 14. 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 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 Scala Aparte 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.
  • 15. 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 = 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
  • 16. 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éricos Nota: 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. 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. }
  • 17. 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.