Logo Studenta

tema8 studentas

¡Este material tiene más páginas!

Vista previa del material en texto

Tema 8: Tipos de datos
Sesión 24: Tipos de datos (1)
miércoles 4 de mayo de 2011
Referencias
• Programming Languages Pragmatics: Capítulo 7, apartados 7.1 (Type 
Systems) y 7.2 (Type Checking)
• Programming in Scala: Capítulo 5 (Basic Types and Operations) y capítulo 11 
(Scala's Hierarchy)
miércoles 4 de mayo de 2011
Indice
• Conceptos generales sobre tipos de datos
• Clasificación de tipos
• Subtipos
• Polimorfismo
• Chequeo de tipos
• Tipos paramétricos en Scala
miércoles 4 de mayo de 2011
Tipos en los lenguajes de programación
• Los bytes que se almacenan en la memoria de un computador no tienen 
tipos de datos: son sólo ceros y unos que se interpretan en función de las 
instrucciones ejecutadas.
• El lenguaje ensamblador tampoco tiene el concepto de tipo, son los 
lenguajes de alto nivel los que lo introducen
miércoles 4 de mayo de 2011
Tipos en los lenguajes de programación
• Propósitos principales de los tipos:
• Proporcionar contexto implícito en muchas ocasiones, de forma que el 
programador no tenga que hacerlo explícito. Aumenta la legibilidad y la 
expresividad de los programas (se pueden decir más cosas con menos 
líneas de código).
• Permiten comprobar errores en programas sintácticamente correctos, 
limitando la cantidad de fallos que se pueden cometer al desarrollar
miércoles 4 de mayo de 2011
Tipos en los lenguajes de programación: contexto 
implícito
• Por ejemplo, en C, la expresión a + b se compila distinto dependiendo del 
tipo a y b.
• En Scala la información de los tipos de datos se utiliza para inferir el tipo de 
las variables y realizar la compilación a byte codes de Java
• La función anterior es muy fácil de leer. El hecho de utilizar una x de tipo 
BigInt permite calcular el factorial de números grandes. En la función se 
utilizan operadores como * y - sin tener que realizar conversiones de tipos. Y 
se utilizan las constantes 0 y 1 que, en principio, son de tipo Int. Parece que 
estamos utilizando tipos normales y no clases.
def factorial(x: BigInt): BigInt = 
 if (x == 0) 1 else x * factorial(x-1)
miércoles 4 de mayo de 2011
Tipos en los lenguajes de programación: contexto 
implícito
• Scala transforma el programa anterior en un programa equivalente, utilizando 
la declaración de la función para saber que x es un objeto de la clase BigInt y 
la clase de Java java.math.BigInteger:
• El primer programa es mucho más expresivo que el segundo.
import java.math.BigInteger
def factorial(x: BigInteger): BigInteger =
 if (x == BigInteger.ZERO)
 BigInteger.ONE
 else
 x.multiply(factorial(x.subtract(BigInteger.ONE)))
miércoles 4 de mayo de 2011
Tipos en los lenguajes de programación: 
comprobación de errores
• Permiten comprobar errores en programas sintácticamente correctos, 
limitando la cantidad de fallos que se pueden cometer al desarrollar
def sumaStrings(a: String, b: String) = {
 a+b
}
var x: Int = 1
var y: Int = 2
sumaStrings(x,y) !!Error!!
miércoles 4 de mayo de 2011
Tipos de variables y de objetos
• Dos interpretaciones para los tipos de datos:
• Tipos declarados para los elementos del lenguaje (variables, parámetros, 
funciones, etc.)
• Tipos de los objetos que se crean en tiempo de ejecución y que se 
almacenan en las variables, se pasan como parámetros o se devuelven en 
una función.
• Ambos elementos pueden ser distintos, como en el siguiente ejemplo de 
Scala:
• El tipo de la variable es AnyVal y el tipo del valor guardado en ella es Double
var x: AnyVal = 1.0
miércoles 4 de mayo de 2011
Tipos de variables y de objetos
• Puede no existir tipo para las variables. En Scheme y otros lenguajes 
débilmente tipeados las variables, parámetros y funciones no se declaran de 
un tipo; su tipo puede cambiar en tiempo de ejecución:
• Pero los objetos guardados en las variables sí que tienen un tipo definido. El 
intérprete comprueba en tiempo de ejecución que los tipos de los objetos y 
las operaciones son compatibles
(define a "hola")
(set! a (cons 1 2))
(define (suma a b)
 (+ a b))
(suma 1.0 2.0)
(suma (/ 2 3) (/ 1 3))
(define a 1.0)
(+ a “hola”)
+: expects type <number> as 
2nd argument, given: “hola”
miércoles 4 de mayo de 2011
Definición de tipo
• Un tipo es un metadato sobre una estructura del lenguaje (variable u objeto) 
que define el tipo de dato que vamos a poder guardar en ella. Esta definición 
especifica de forma implícita el tipo de operaciones que vamos a poder 
realizar sobre los datos.
miércoles 4 de mayo de 2011
Tipos en Scala
• Scala es un lenguaje estáticamente tipeado: todas las variables, parámetros, 
funciones, etc. tienen un tipo definido
• La forma de declarar el tipo de una estructura es utilizando dos puntos (:)
var x: String = "Hola"
def makePlural(c: String): String = {
 c+"s"
}
miércoles 4 de mayo de 2011
Tipos en Scala
• Scala realiza una inferencia de tipos a partir de las expresiones y podemos no 
declarar los tipos de las estructuras (en los parámetros de las funciones es 
obligatorio):
• La inferencia de tipos de Scala es una idea que toma de los lenguajes de 
programación funcional ML y Haskell.
var x = "Hola"
def makePlural(c: String) = {
 c+"s"
}
miércoles 4 de mayo de 2011
Sistemas de tipos
• Un sistema de tipos consiste en:
• Un mecanismo para definir tipos y asociarlos con ciertas construcciones 
del lenguaje
• Un conjunto de reglas que definen la equivalencia de tipos, compatibilidad 
de tipos y la inferencia de tipos.
miércoles 4 de mayo de 2011
Chequeo de tipos
• El chequeo de tipos es el proceso por el que se asegura que el programa 
obedece las reglas de compatibilidad de tipos
• Un error se denomina una colisión de tipos (type clash en inglés)
• El chequeo de tipos se puede hacer en tiempo de compilación (lenguaje 
estáticamente tipeado) y/o en tiempo de ejecución
• Por ejemplo, lenguajes como Ada, Pascal, Scala o Java realizan una gran parte 
del chequeo de tipos en tiempo de compilación
• Lenguajes de script como Python, Ruby o Scheme realizan el chequeo de tipos 
en tiempo de ejecución
• Otros lenguajes como ensamblador o C no chequean los tipos en tiempo de 
ejecución
miércoles 4 de mayo de 2011
Clasificación de tipos
• Booleano
• Carácter
• Tipos numéricos
• Tipos enumerados
• Tipos de subrango
• Tipos compuestos
miércoles 4 de mayo de 2011
Clasificación de tipos
• Tipos compuestos:
• Registros
• Registros variantes (uniones)
• Arrays
• Conjuntos
• Listas
• Ficheros
miércoles 4 de mayo de 2011
Booleanos
• Los tipos booleanos (true y false) existen en la mayoría de lenguajes de 
programación
• En C no; 1 representa true y 0 false
• En Scala:
var finalLista: Boolean = false
miércoles 4 de mayo de 2011
Carácter
• En los lenguajes más antiguos (C, Pascal,…) el tipo carácter tiene un byte de 
tamaño y guardan el código ASCII del carácter
• Los lenguajes más modernos (Java, C#, Scala, …) tienen dos bytes y 
guardan el código Unicode del carácter
• En Scala:
var a: Char = 'A'
var c = '\101' ; número octal 
var c = '\u0041' ; número hexadecimal
var backslash = '\\'
miércoles 4 de mayo de 2011
Tema 8: Tipos de datos
Sesión 25: Tipos de datos (2)
Referencias
• Programming Languages Pragmatics: Capítulo 7, apartados 7.1 (Type 
Systems) y 7.2 (Type Checking)
• Programming in Scala: Capítulo 5 (Basic Types and Operations) y capítulo 11 
(Scala's Hierarchy)
Indice
• Tipos numéricos, enumerados, de subrango
• Sobrecarga 
• Coerción 
• Polimorfismo
• Inferencia de tipos
Tipos numéricos
• La mayoría de lenguajes diferencian entre distintos tipos numéricos en 
función de la precisión (enteros o punto flotante) y del tamaño (y la cantidad 
de números que puede representar)
• En Scala podemos usar los siguientes tipos numéricos y precisiones:
Tipo de valor Rango
Byte Entero de 8-bit en complemento a 2 (-27 hasta 27-1, inclusive)
Short Entero de 16-bit en complemento a 2 (-215 hasta 215-1, inclusive)
Int Entero de 32-bit en complemento a 2 (-231 hasta 231-1, inclusive)
LongEntero de 64-bit en complemento a 2 (-263 hasta 263-1, inclusive)
Float Punto flotante IEEE 754 precisión sencilla 32-bit
Double Punto flotante IEEE 754 precisión doble 64-bit
Tipos enumerados
• Introducidos por N. Wirth en el diseño de Pascal
• Facilitan la legibilidad de los programas y permiten que el compilador 
chequee errores
• Ejemplo en Pascal:
• Uno de los problemas de los tipos enumerados es la conversión en tipos 
"portables" que puedan ser entendidos en otros sistemas (como bases de 
datos o XML)
type weekday = (sun, mon, tue, wed, thu, fri, sat);
Tipos enumerados
• Es habitual que los valores del tipo enumerado se identifiquen con los 
enteros (del 0 en adelante) o con Strings correspondientes con su 
identificador
• En Java cualquier valor enumerado puede convertirse a estos dos tipos con 
los métodos name y ordinal:
public enum Day {
 SUNDAY, MONDAY, TUESDAY, WEDNESDAY, 
 THURSDAY, FRIDAY, SATURDAY 
}
Day day = SUNDAY;
String str = day.name();
int a = day.ordinal();
Enumerados en Scala
• Los tipos enumerados son subclases de la clase scala.Eumeration
• Hay que definir un object (lo veremos más adelante, en POO en Scala) que 
extiende Enumeration:
• El tipo de dato es Color.Value
object Color extends Enumeration {
 val Red, Green, Blue = Value
}
var c = Color.Red
Color.Value = Red
Enumerados en Scala
• Es posible modificar el nombre asociado a cada constante:
object Direction extends Enumeration {
 val North = Value("Norte")
 val East = Value("Este")
 val South = Value("Sur")
 val West = Value("Oeste")
}
var d = Direction.South
print(d)
Tipos de subrango
• Al igual que los tipos enumerados, fueron introducidos en Pascal
• Un subrango es un tipo cuyos valores componen un subconjunto contiguo de 
los valores de algún tipo de datos discreto
• En Pascal es posible definir rangos de enteros, caracteres, enumeraciones e 
incluso otros subrangos:
• Los tipos de subrangos son útiles para asegurar la corrección semántica de 
los programas
type test_score = 0..100;
type work_day = mon..fri;
Tipos de subrango
• En Scala no existen estos subrangos, sólo existe la clase Range de la que 
podemos crear instancias con un rango de valores. Pero no tipos:
var r = 1 to 100
Sobrecarga, coerción y polimorfismo 
(págs. 146-151 PLP)
• Los conceptos de sobrecarga (overloading), coerción (coercion) y 
polimorfismo (polymorphism) están relacionados y es fácil confundirlos
• Todos tienen que ver con la idea aumentar la expresividad de los lenguajes 
permitiendo que las mismas líneas de código tengan significados distintos 
dependiendo de los tipos de las variables implicadas
• Son también conceptos técnicos que dependen cómo se procesa el lenguaje, 
de forma interpretada o de forma compilada. Por ejemplo, la sobrecarga es 
una técnica concreta relacionada con el proceso de compilación de un 
lenguaje, mientras que el polimorfismo es un término más general que se 
aplica a características del propio lenguaje independientemente de si se 
compilan o se interpretan los programas.
Sobrecarga
• Se realiza una sobrecarga cuando tenemos distintas definiciones de un 
mismo término (constante, función, operador) asociados a distintos tipos y 
dependiendo del tipo de las variables o las expresiones se decide en tiempo 
de compilación por una definición u otra:
• El compilador decide que el primer operador + se debe compilar a una suma 
de enteros y el segundo a una suma de reales. El programador de C no tiene 
que usar operadores distintos.
//Ejemplo en C
int sobrecarga(int a, int b, double x) {
 double c = a+b;
 return(c+x);
}
Sobrecarga
• El compilador avisa con un error si no tiene suficiente información para 
decidir (ejemplo en Ada):
• Las constantes dec y oct están repetidas en las dos enumeraciones. Se 
asigna la constante correcta gracias al contexto definido por el tipo de las 
variables mo y pb. El la última instrucción el compilador da un error porque no 
hay información suficiente para decidir el tipo de oct.
declare
 type month is (jan, feb, mar, apr, may, jun,
 jul, aug, sep, oct, nov, dec);
 type num_base is (dec, bin, oct, hex);
 mo: month;
 pb: num_base
begin
 mo := dec; -- el mes diciembre (mo tiene de tipo month)
 pb := oct; -- octal (pb es de tipo num_base)
 print(oct); -- error! contexto insuficiente para decidi
Sobrecarga
• Lenguajes como Java o C# obligan a explicitar la enumeración que se utiliza:
• En la mayoría de lenguajes orientados se pueden sobrecargar los métodos 
con distintos tipos de parámetros.
mo = month.dec;
pb = num_base.oct;
Sobrecarga en Scala
• Scala técnicamente no tiene sobrecarga de operadores, porque hemos 
visto que es un lenguaje orientado a objetos y no tiene operadores en el 
sentido tradicional
• Cuando escribimos a+b Scala traduce la expresión a (a).+(b): se ejecuta 
el método + sobre el objeto a pasando como parámetro b
• Sí que tiene sobrecarga de métodos: en el caso anterior el compilador 
decidirá llamar al método + correspondiente según el tipo del parámetro.
• En Scala es legal utilizar los identificadores +, -, etc. como nombres de 
métodos, por lo que podemos utilizarlos en nuevas clases y después con 
variables de estas clases:
• En Scala es legal utilizar los identificadores +, -, etc. como nombres de 
métodos, por lo que podemos utilizarlos en nuevas clases y después con 
variables de estas clases:
Sobrecarga en Scala
class Rational(n: Int, d: Int) {
 val numer
 val denom
 def + (otro: Rational): Rational = 
 new Rational(
 numer * otro.denom + otro.numer * denom,
 denom * otro.denom
 )
 ...
}
val x = new Rational(1,2)
val y = new Rational(2,3)
x + y
Coerción
• De alguna manera lo contrario de la sobrecarga
• Se aplica a funciones y métodos
• Es el proceso por el que un compilador convierte un valor de un tipo en otro 
tipo cuando el contexto lo necesita (por ejemplo, cuando se usa como 
parámetro)
• El código anterior es correcto en C; se convierten los valores j y k a doubles 
y después el valor devuelto por min(double) se vuelve a convertir en int.
double min(double x, double y) {...}
...
int i,j,k
...
i = min(j,k)
Conversión de tipos en Scala
• En Scala es posible definir una conversión de tipos implícita de un tipo A a 
otro B con la palabra clave implicit:
• Cuando hay un error en el chequeo de tipos, el compilador de Scala intenta 
resolverlo buscando alguna función implícita que realice la transformación
• El objeto scala.Predef, que es importado implícitamente en cualquier 
programa de Scala, define conversiones estándar, por ejemplo de Int a 
Double: 
implicit def double2String(x:Double):String = x.toString
var a: String = 2.0
implicit def int2double(x: Int): Double = x.toDouble
var y: Int = 2
var x: Double = y
Polimorfismo
• Se utiliza el polimorfismo cuando tenemos código (estructuras de datos, 
clases, funciones y métodos, etc.) que puede trabajar con valores de 
múltiples tipos.
• Los tipos deben tener características comunes que son las usadas por el 
código polimórfico
• En los lenguajes débilmente tipeados (Ruby, Python) el polimorfismo se suele 
resolver en tiempo de ejecución
• En los lenguajes estáticamente tipeados podemos diferenciar dos tipos: 
polimorfismo paramétrico y polimorfismo de subtipos
Polimorfismo
• Polimorfismo paramétrico: el código toma un tipo como parámetro, bien 
explícita o implicitamente. A esta característica también se conoce como 
genéricos en muchos lenguajes (Ada, C++, Java, Scala)
• Polimorfismo de subtipos: el código está diseñado para trabajar con un tipo 
T, pero el programador puede definir extensiones o refinamientos de T 
(subtipos de T) con los que el código también funcionará correctamente
Clases genéricas en Scala
class Stack[T] {
 var elems: List[T] = Nil
 def push(x: T) { elems = x :: elems }
 def top: T = elems.head
 def pop() { elems = elems.tail}
}
val a = new Stack[String]
a.push("Madrid")
a.push("Roma")
a.pop
a.topPolimorfismo de subtipos
• La forma de polimorfismo más habitual en los lenguajes orientados a objetos
• Se utiliza un polimorfismo de subtipos cuando una variable o parámetro se 
define de un tipo T y el código puede guardar cualquier subtipo S de T (el 
hecho de que S es un subtipo de T se suele simbolizar de la siguiente forma: 
S < T).
• Los subtipos del mismo tipo base son compatibles
• Principio de sustitución de subtipos:
Si S es un subtipo de T, entonces los objetos de tipo T pueden ser 
reemplazados con objetos de tipo S sin alterar ninguna de las propiedades 
del programa.
Jerarquía de tipos de Scala
• En Programación Orientada a Objetos es posible especializar una clase A 
definiendo otra clase B que la extiende. En ese caso el tipo B es un subtipo 
de A (lo veremos con más detalle en el próximo tema)
Restricciones en tipos de subclases
(Chapter 20, Programming in Scala)
• En Scala es posible definir restricciones sobre los tipos en las clases 
abstractas que van a ser extendidas
class Food
abstract class Animal {
 type SuitableFood <: Food
 def eat(food: SuitableFood)
}
class Grass extends Food
class Cow extends Animal {
 type SuitableFood = Grass
 override def eat(food: Grass) {}
}
Restricciones en tipos de subclases
(Chapter 20, Programming in Scala)
• Las subclase concretas de Animal tienen que definir el tipo de SuitableFood 
(es como un tipo paramétrico)
• La restricción es que debe ser un subtipo (con el operador <:) de Food
• La clase Cow define como SuitableFood el tipo Grass
• No podemos definir subclases de Animal que sobreescriban el método eat 
con un parámetro de un tipo que no sea subclase de Food. El siguiente 
código sería erróneo:
class Dog extends Animal {
 type SuitableFood = Int // error! Int no es subtipo de Food
 override def eat (food: Int) {} 
}
Polimorfismo en tiempo de ejecución
• El polimorfismo en tiempo de ejecución permite invocar métodos y funciones 
de forma de forma dinámica, teniendo en cuenta el tipo del objeto 
almacenado en una variable y no el tipo de la variable
• En el siguiente ejemplo, el codigo polimórfico es print a.diAlgo ya que 
se ejecuta con objetos de tipo Dog y Cat. No se sabe hasta el momento de 
ejecución (run time) de qué tipo es el objeto en la lista de animales
Polimorfismo en tiempo de ejecución
abstract class Animal {
 def diAlgo(): Unit
}
class Perro extends Animal {
 def diAlgo() = {
 println("Guau!!")
 }
}
class Gato extends Animal {
 def diAlgo () = {
 println("Miau!!")
 }
}
var animales: List[Animal] = List(new Perro, new Perro, new Gato)
for (a <- animales)
 a.diAlgo
Guau!!
Guau!!
Miau!!
Especializando un objeto de una clase concreta
• Supongamos que la clase derivada añade algún comportamiento a la clase 
padre. Por ejemplo, definimos el método limpiate() en la clase Gato y el 
método traePalo() en la clase Perro:
class Perro extends Animal {
 def diAlgo() = {
 println("Guau!!")
 }
 def traePalo() = {
 println("Tírame el palo")
 }
}
class Gato extends Animal {
 def diAlgo () = {
 println("Miau!!")
 }
 def limpiate() = {
 println("Me estoy acicalando");
 }
}
Especializando un objeto de una clase concreta
• Los métodos sólo los podremos llamar en variables de cada clase
• Por ejemplo, supongamos que guardamos en una variable del tipo de la clase 
padre (Animal) un objeto de la clase derivada (Gato o Perro). Sería un error 
llamar al método definido en la clase derivada. También es un error asignarlo 
a una variable del tipo derivado:
var a: Animal = new Gato
// Error, método no definido en Animal:
a.limpiate()
// Error, type mismatch:
var g: Gato = a
Especializando un objeto de una clase concreta
• Podemos hacerlo, sin embargo, si convertimos la variable en otra del tipo 
original. Scala define el método asInstanceOf para ello:
• También se puede utilizar una sentencia match para identificar el tipo de 
animal:
a.asInstanceOf[Gato].limpiate
var a: Animal = new Gato
a match {
 case g: Gato => g.limpiate()
 case p: Perro => p.traePalo()
}
me estoy acicalando
Inferencia de tipos
• Scala y otros lenguajes permiten no definir los tipos de las variables cuando 
pueden ser inferidos por el compilador
• La inferencia de tipos de Scala es una idea que toma de los lenguajes de 
programación funcional ML y Haskell.
• Es necesario especificar el tipo de los parámetros de una función y de las 
funciones recursivas
Inferencia de tipos
• La inferencia de tipos tiene que tener en cuenta las relaciones de subtipos:
• ¿Cuál es el tipo de prueba?
• El algoritmo de inferencia de tipos más conocido es el denominado Hindley-
Milner y está basado en un proceso de satisfacción de restricciones 
utilizando la unificación.
abstract class Animal
class Perro extends Animal
class Gato extends Animal
def prueba (a: Int) = {
 if (a > 10) 
 new Gato
 else new Perro
}

Continuar navegando

Materiales relacionados

207 pag.
Java-pdf

Albert Einstein

User badge image

FREDY PICHIHUA

17 pag.
Unidad 1

SIN SIGLA

User badge image

carlos galarcio