Tipos de Datos Estructurados - Departamento de Informática

Anuncio
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Tipos de Datos Estructurados
Departamento de Informática
Universidad Nacional de San Luis, Argentina
https://sites.google.com/site/disenioyparadigmas
e-mail: [email protected]
29 de agosto de 2016
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Tipos de datos estructurados
Roggero
Estructura de datos (objeto de datos estructurado):
contiene otros objetos de datos (componentes) que pueden
ser elementales o estructurados.
Algunos tipos de datos estructurados importantes:
• Arreglos
• Slices
• Registros
• Strings (cadenas de caracteres)
• Conjuntos
• Punteros
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Especificación de tipos de datos
estructurados
Roggero
La especificación en estos casos suele incluir los siguientes
atributos:
• El número de componentes: ¿estructura de tamaño fijo
o variable?.
• El tipo de cada componente: ¿estructura de datos
homogénea o heterogénea?.
• Los nombres usados para seleccionar las
componentes: ¿subíndices, identificadores definidos
por el programador o componentes particulares?
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Especificación de tipos de datos
estructurados
• El número máximo de componentes (en estructuras de
datos de tamaño variable).
• La organización de las componentes: ¿secuencia lineal
de las componentes o formas multidimensionales?.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Operaciones sobre estructuras de
datos
• La selección (o acceso) de componentes: ¿directa o
secuencial?.
• Operaciones con estructuras de datos completas: la
mayoría de los lenguajes proveen un número limitado
de este tipo de operaciones (sumar 2 arreglos, asignar
un registro a otro, la unión de conjuntos).
• La inserción/supresión de componentes: modifican el
número de componentes.
• La creación/destrucción de estructuras de datos.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Implementación de tipos de datos
estructurados
Roggero
La representación en memoria para una estructura de
datos incluye:
1
Memoria para las componentes de la estructura.
2
Un descriptor (opcional) con algunos (o todos) los
atributos de la estructura.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Implementación de tipos de datos
estructurados
Representacion secuencial
Descriptor
Representacion encadenada
Descriptor
(Opcional)
Componente
Componente
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Implementación de operaciones sobre
estructuras de datos
Roggero
En general deben ser simuladas por software. Es
fundamental el rol de la selección (acceso) de componentes
(directa o secuencial), que está influenciada por el tipo de
representación:
Con representación secuencial:
• la selección directa involucra un cálculo
dirección-base-más-desplazamiento (db+d).
• el acceso a una secuencia de componentes involucra:
seleccionar la primera componente de la serie en base
al cálculo db+d
2 para avanzar a siguiente componente, sumar tamaño
componente actual a su ubicación.
1
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Con representación encadenada:
• la selección directa involucra seguir la cadena hasta la
componente deseada.
• la selección de una secuencia de componentes
involucra:
1
2
acceder a la primera componente como antes.
seguir el puntero a la siguiente componente por cada
selección subsiguiente.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Administración de memoria y
estructuras de datos
Durante el tiempo de vida de un OD, se pueden crear y
destruir distintos pasos de acceso a el.
Cuando los tiempos de vida de los OD y de sus pasos de
acceso no coinciden pueden surgir dos tipos de problemas:
Basura y Referencias desactivadas.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Arreglos (unidimensionales o vectores)
Roggero
Estructuras de datos integradas por un número fijo de
componentes del mismo tipo organizados como una serie
lineal simple, cuyas componentes pueden ser accedidas
mediante un subíndice, un entero (o valor enumerado) que
indica su posición en la serie.
Atributos de un vector:
• El número de componentes.
• El tipo de datos de las componentes.
• Valores de subíndice válidos
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Arreglos (unidimensionales o vectores)
Roggero
Ejemplos (en Pascal y C):
var a: array [-5 .. 5] of real;
float a[10];
Algunos lenguajes incluyen arreglos heterogéneos, es
decir cuyas componentes no necesitan ser todas del
mismo tipo, por ejemplo Perl, Python, JavaScript y
Ruby
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Algunas operaciones con vectores
Roggero
• Acceso (y modificación) de una componente mediante
subindización.
• Operaciones aritméticas con vectores completos
(pocos lenguajes las proveen).
• Creación y destrucción de vectores.
• No se permite la inserción y supresión de
componentes.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Inicialización de Arreglos
Roggero
Algunos lenguajes permiten la inicialización de arreglos en
el punto de declaración:
• C, C + +, Java, C#, ejemplo:
int list [] = {4, 5, 7, 83}
• Arreglos de strings in C y C + +:
char *names [] = {“Bob”, “Jake”, “Joe”}
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Representación en memoria de
vectores
Roggero
Vector
Descriptor
Limite Inferior
LS
Limite Superior
Entero
E
A[LI]
A[LI+1]
Componentes
A[LS−1]
A[LS]
Tipo de datos
LI
Tipo de datos (componente)
Tamano del componente
α
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Acceso a la i-ésima componente de un
vector
dir (A[i]) = α + (i − LI) × E
Pero esta fórmula la podemos escribir como:
dir (A[i]) = (α − LI × E) + i × E
Notar que una vez que se asigna espacio (α − LI × E) es
una constante que se puede llamar K , entonces la fórmula
de acceso se reduce a:
dir (A[i]) = K + i × E
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Acceso en base al origen virtual (OV)
Roggero
Consideremos ahora la dirección A[0] del vector:
dir (A[0]) = (α − LI × E) + 0 × E
dir (A[0]) = (α − LI × E)
dir (A[0]) = K
K representa la dirección de la componente A[0] aunque
ésta no exista, debido a que el elemento 0 puede no ser
parte del arreglo, esta dirección es llamada origen virtual
(OV).
OV = dir (A[0]) = α − LI × E
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Acceso en base al origen virtual (OV)
Roggero
Entonces en base al OV podemos reescribir la fórmula de
acceso a una componente como:
dir (A[i]) = OV + i × E
La verificación de que i está entre LI y LS debe preceder el
uso de la fórmula, es por eso que estos valores deben estar
presentes en el descriptor en tiempo de ejecución.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Ubicación del origen virtual
Roggero
La ubicación relativa del OV respecto a los elementos del
arreglo, puede variar dependiendo de los subíndices
utilizados.
Ejemplo: supongamos α = 1000 y E = 2. Dibujar la
posición del origen virtual respecto a los elementos de los
siguientes arreglos de 5 enteros:
A: array [3..7] of integer;
A: array [-2..2] of integer;
A: array [0..4] of integer;
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Almacenamiento separado para
descriptor y componentes
Roggero
A[LI]
α
A[LI+1]
Vector
OV
Origen Virtual
LI
Limite Inferior
LS
Limite Superior
Entero
E
A[LS−1]
A[LS]
Tipo de datos
Tipo de datos (componente)
Tamano del componente
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Arreglos multidimensionales
Roggero
Los arreglos de una dimensión que hemos visto se
denominan vectores. Sin embargo, los arreglos pueden
tener más de una dimensión.
Para el caso bi-dimensional se suele usar el término matriz
con sus correspondientes filas y columnas.
Ejemplo:
1
3
5
2
4
6
3
5
7
4
6
8
En general, es posible extender esta idea a arreglos
n-dimensionales, con n > 2.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Implementación de arreglos
bi-dimensionales (matrices)
Roggero
Es directa si consideramos una matriz como un vector de
vectores. Un arreglo tri-dimensional puede ser considerado
un vector cuyas componentes son vectores de vectores,
etc.
Primera pregunta que surge al implementar una matriz:
• ¿es un vector de filas? (representación por filas u
orden por filas)
• ¿es un vector de columnas? (representación por
columnas u orden por columnas)
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Representación por filas de una matriz
Roggero
var A: array [1 .. 3, 2 .. 5] of integer;
Matriz
LI1 (= 1)
LS1 (= 3)
2
1
1
3
2
4
3
5
LI2 (= 2)
LS2 (= 5)
4
2
3
4
5
6
3
5
6
7
8
Entero
A[1,2]
E
1
A[1,3]
2
A[1,4]
3
A[1,5]
4
A[2,2]
3
A[2,3]
4
A[2,4]
5
A[2,5]
6
A[3,2]
5
A[3,3]
6
A[3,4]
7
A[3,5]
8
Tipo de datos
Limite Inferior 1
Limite Superior 1
Limite Inferior 2
Limite Superior 2
Tipo de datos (componente)
Tamano del componente
α
Primera fila
Segunda fila
Tercera fila
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Acceso a la componente A[i, j] de una
matriz A
A partir de α debo ”saltar” (i − LI1) filas de tamaño S y
(j − LI2) elementos de tamaño E.
S = cantidad de componentes por fila × tamaño de
componente = (LS2 − LI2 + 1) × E
dir (A[i, j]) = α + (i − LI1) × S + (j − LI2) × E
(4)
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Acceso a la componente A[i, j] de una
matriz A
Cuando LI1 = 0 y LI2 = 0 la fórmula de acceso se
simplifica:
dir (A[i, j]) = α + i × S + j × E (5)
Este es el caso en los lenguajes que consideran siempre el
0, 0 como la componente inicial.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Acceso en base al origen virtual (OV)
Roggero
Con la misma idea utilizada con los vectores, el origen
virtual en este caso sería:
OV = dir (A[0, 0]) = α − (LI1 × S) − (LI2 × E)
OV representa la dirección de la componente A[0, 0] si ella
existiera en la matriz.
En base al OV podemos redefinir la fórmula de acceso para
una componete cualquiera, a partir de la fórmula (4):
dir (A[i, j]) = OV + i × S + j × E
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Representación por columnas de una
matriz
Roggero
var A: array [1 .. 3, 2 .. 5] of integer;
Matriz
LI1 (= 1)
LS1 (= 3)
2
1
1
3
2
4
3
5
LI2 (= 2)
LS2 (= 5)
4
2
3
4
5
6
3
5
6
7
8
Entero
A[1,2]
E
1
A[2,2]
3
A[3,2]
5
A[1,3]
2
A[2,3]
4
A[3,3]
6
A[1,4]
3
A[2,4]
5
A[3,4]
7
A[1,5]
4
A[2,5]
6
A[3,5]
8
Tipo de datos
Limite Inferior 1
Limite Superior 1
Limite Inferior 2
Limite Superior 2
Tipo de datos (componente)
Tamano del componente
α
Primera columna
Segunda columna
Tercera columna
Cuarta columna
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Slices (rebanada)
Una rebanada es alguna subestructura de un arreglo, sólo
son útiles en los lenguajes que tienen operaciones con
arreglos.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Ejemplo Slices
Roggero
Sean las siguientes declaraciones en Python:
vector = [2, 4, 6, 8, 10, 12, 14, 16]
mat = [[1, 2, 3],[4, 5, 6],[7, 8, 9]]
La sintaxis de los slices en Python es un par de expresiones
numéricas separadas por :, es decir, vector[3:6].
Una fila de una matriz se especifica dando solo un índice,
por ejemplo, mat[1], corresponde a la segunda fila de la
matriz.
También es posible especificar una parte de una fila,
mat[0][0:2], en este caso se refiere al primer y segundo
elemento de la primera fila, o sea [1, 2].
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Registros
Roggero
Especificación y Sintaxis
Son estructuras de datos lineales y de longitud fija. Difieren
de los arreglos en dos aspectos:
• Las componentes de los registros pueden ser
heterogéneas.
• Las componentes se designan con nombres simbólicos
(identificadores).
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Atributos de Registros
Roggero
• El número de componentes.
• El tipo de datos de cada componente.
• El selector que se usa para nombrar cada componente.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Operaciones
Roggero
La operación básica es la selección de componentes. La
manera de acceder a una componente es vía un nombre y
no un valor computado.
Por lo general son pocas las operaciones sobre registros
completos, la más común es la asignación de registros de
estructura idéntica.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Implementación de Registros
La representación de almacenamiento consiste en un sólo
bloque secuencial de memoria donde se guardan las
componentes en serie.
Ejemplo:
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Implementación de Registros (Cont.)
Roggero
La selección de componentes es sencilla debido a que los
nombres de campos se conocen durante la traducción.
Además la declaración de los registros permiten determinar
el tamaño de las componentes y su posición dentro del
bloque de almacenamiento durante la traducción.
Fórmula de acceso para la i-ésima componente:
dir (R.i) = α +
O directamente:
!i−1
j=1 (tamR.j)
dir (R.i) = α + Ki
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Strings (cadenas de caracteres)
Roggero
Objeto de datos compuesto de una secuencias de
caracteres.
Un aspecto de diseño importante a tener en cuenta es:
La manera en que el lenguaje soporta los strings:
• ¿Es un tipo primitivo? (Java, Pascal, Ada, JavaScript,
Snobol4)
• ¿Es un arreglo de caracteres? (C, C++)
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Especificación y sintaxis:
Roggero
• Strings de longitud fija declarada (Cobol, Strings en
Java, Pascal)
• Strings de longitud variable (con límite declarado) (C y
C++)
• Strings de longitud variable (sin límite) (JavaScript,
Snobol4)
El lenguaje Ada provee los 3 tipos.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Algunas operaciones con strings
Roggero
• Asignación (copia)
• Concatenación
• Comparación (relacionales)
• Longitud
• Selección de substrings por posición o concordancia
de patrones
• Conversión a números
• Selección, inserción y supresión de un caracter
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Implementación de strings de longitud
fija
Roggero
La longitud es fija y se establece cuando se crea el objeto.
Si no se utilizan todas las posiciones, se completan los
espacios restantes.
R
E
L
A
T
I
V
I
D A
D
"" "" ""
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Implementación de strings de longitud
variable (con límite)
Se reserva espacio para una longitud máxima de string,
pero la cadena actual puede ser más corta.
Se debe incluir información que permita determinar cual es
el último caracter del string actual.
Alternativas:
• Almacenar longitud actual y máxima
11 14
R
E
L
A
T
I
V
I
D
A
D
• Almacenar delimitador de fin de string (como en C)
R
E
L
A
T
I
V
I
D A
D
\0
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Roggero
Implementación de strings de longitud
variable (ilimitada)
El string puede tener cualquier longitud, y variar
dinámicamente por la inserción o supresión de caracteres.
Alternativas de implementación:
• Lista encadenada de caracteres. Fácil inserción y
supresión de caracteres.
• Se puede usar un arreglo contiguo de caracteres que
contenga el string. Es el método usado en C.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Conjuntos
Roggero
Un conjunto es un objeto de datos que contiene una
colección no ordenada de valores distintos:
Las operaciones básicas sobre conjuntos son:
• Pertenecia
• Inserción y eliminación de valores.
• Unión, intersección y diferencia de conjuntos.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Conjuntos
Roggero
Implementación
En los lenguajes de programación, se usa, a veces, la
palabra conjunto para nombrar una estructura de datos que
representa un conjunto ordenado. Un conjunto ordenado es
en realidad una lista, de la que se han eliminado los valores
duplicados.
Pero si trabajamos con conjuntos no ordenados debemos
saber que admiten dos representaciones de
almacenamiento. Por ejemplo el type set de Pascal.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Conjuntos en Pascal
Roggero
Sintaxis: TYPE TipoSet = Set of tipo;
Ejemplo:
type dia = (lu,ma,mi,ju,vi,sa,dm);
Frutas = (limon,naranja,uva,pera,platano);
conj-caract = Set of Char;
digitos = Set of 0..9;
dias = Set of dia;
clase-fruta = Set of frutas;
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Conjuntos en Pascal
Roggero
Y pueden declararse variables de tipo Set de la siguiente
manera:
var laborable : dias;
letras : conj-caract;
conj-num : digitos;
La asignación de una variable de este tipo puede ser:
laborable := [lu, ma, mi, ju, vi];
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Conjuntos
Roggero
Representación de conjuntos por cadenas de bits: es
apropiada cuando se sabe que el tamaño del universo
subyacente de valores es pequeño.
Si tenemos N elementos en el universo e 1 , e2 , .., en , un
conjunto de elementos elegido de entre este universo se
puede representar por medio de una cadena de bits de
longitud N, donde el i-ésimo elemento de la cadena es 1 si
ei está en el conjunto y 0 en caso contrario.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Conjuntos
Roggero
Representación de conjuntos codificada por distribución
pseudo-aleatoria (hash): se usa cuando el universo
subyacente de valores posibles es grande.
Se asigna memoria al menos dos veces más de lo que se
espera usar, los elementos se dispersan al azar. El truco
está en guardar cada nuevo elemento de manera tal que,
su presencia o ausencia se pueda determinar después, de
inmediato, sin realizar una búsqueda. Esto se realiza
mediante la función de hash.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Punteros
Roggero
Existen ciertos tipos de datos como los punteros, que bien
podrían considerarse elementales, dado que almacenan
direcciones, pero su implementación usualmente involucra
una organización de estructuras de datos complejas para el
compilador.
Se necesitan varias características en los lenguajes de
programación para permitir el uso de punteros:
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Punteros
Roggero
• Un apuntador de tipo elemental de datos. Un objeto de
datos de tipo apuntador contiene la dirección de otro
objeto de datos, o un valor especial NULL (o NIL).
• Una operación de creación para objetos de datos de
tamaño fijo, como arreglos, registros y tipos
elementales. En C el malloc o el new en C++. Y si no
se provee la desasignación implícita también se
requiere de una operación explícita, free en C y
delete en C++.
• Una operación de desreferenciación para valores de
punteros. En C *.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Especificación de Punteros
Roggero
Un tipo de datos puntero define una clase de objetos de
datos cuyos valores son direcciones de otros objetos de
datos.
Un único objeto de datos de tipo puntero se puede tratar de
dos formas:
1
Los punteros sólo pueden hacer referencia a objetos
de datos de un solo tipo. Es el enfoque que se usa en
Pascal, C y Ada.
2
Los punteros pueden hacer referencia a objetos de
datos de distinto tipo. Permitir que un puntero apunte a
objetos de datos de tipos variables en diferentes
momentos durante la ejecución del programa. Enfoque
utilizado en Smalltalk.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Punteros
Roggero
Operaciones:
1
La operación de creación asigna almacenamiento para
un objeto de datos de tamaño fijo y también crea un
puntero al nuevo objeto de datos. En Pascal y Ada se
llama new.
2
La operación de selección permite seguir un valor de
apuntador para alcanzar el objeto de datos designado.
En C, por ejemplo la operación de selección que sigue
a un puntero hasta su objeto designado se escribe ∗.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Implementación de Punteros
Roggero
Un objeto de datos de tipo puntero se representa como una
ubicación de memoria que contiene la dirección de otra
ubicación de memoria. La dirección, es la dirección base
del bloque de almacenamiento que representa al objeto de
datos al que el puntero apunta.
En general, se utilizan dos representaciones para los
objetos de datos de tipo puntero:
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Implementación de Punteros (Cont.)
Roggero
• Direcciones absolutas: un valor puntero se puede
representar como la dirección de memoria real del
bloque de almacenamiento para el objeto de datos.
• Direcciones relativas: un valor puntero se puede
representar como un desplazamiento, respecto a la
dirección base, de algún bloque de
almacenamiento(del heap) más grande dentro del cual
el objeto está asignado.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Implementación de Punteros (Cont.)
Roggero
Direcciones Absolutas:
Ventajas:
1
Se puede asignar espacio para los objetos de datos en
cualquier parte de la memoria.
2
La operación de selección es más efeciente, porque el
valor del puntero mismo proporciona acceso directo al
objeto de datos.
Desventaja: la administración de memoria es más difícil,
dado que ningún objeto de datos se puede mover dentro de
la memoria si existe un apuntador a él.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Implementación de Punteros (Cont.)
Roggero
Direcciones Relativas:
Se requiere la asignación inicial de un bloque de
almacenamiento, dentro del cual tiene lugar la asignación
del objeto de dato puntero, por medio de la operación de
creación.
Ventaja: se puede mover el bloque completo de memoria,
en cualquier momento, sin invalidar ningún puntero.
Desventaja: la selección es más costosa porque se debe
sumar el desplazamiento a la dirección base del área.
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Tipo Referencia
Roggero
Una variable de tipo referencia es similar a un puntero,
pero con una diferencia fundamental:
Un puntero se refiere a una dirección en memoria,
mientras que una referencia se refiere a un objeto o un
valor en memoria.
Las referencias en Java son identificadores de instancias de
alguna clase en particular.
Ejemplo:
String cad; //referencia a un objeto de la
clase String
Punto p; //referencia a un objeto de la
clase Punto
int[] var-arreglo; //referencia a un arreglo
de enteros
Diseño y
Paradigmas
de Lenguajes
- Año 2016
Tipo Referencia
Roggero
Una referencia es básicamente un puntero a un objeto,
pero, a diferencia de los punteros de otros lenguajes, no
obtenemos el valor al que apunta el puntero, sino que el
propio Java es el que se encarga de obtener el objeto.
Al definir una variable de un tipo de referencia, lo que
hacemos es permitir que dicha variable apunte a instancias
de dicha clase, es decir, objetos de dicho tipo. Cuando
declaramos una variable de este tipo y no la iniciamos,
tomará el valor constante NULL.
Descargar