28 jul 2010

Tipos de variables y datos -PROGRAMACIÓN INFORMÁTICA GENERAL-

PARTE 2:

tipos de variables y datos
_____________________________________________________

-A partir de ahora trataremos las posiciones de memoria en hexadecimal, sin embargo las colecciones en decimal. Ésto es para simplificar, así de una mirada se sabe si lo que tenemos delante es una colección de números de tamaño variable num(5), o un registro dentro de un chip MEM(0050h). De esta forma relaccionaremos la matemática diréctamente con la informática y la electrónica. Digamos que "calcularemos como una máquina". Utilizaremos nuevos conceptos, o mejor dicho, nuevas formas de usar conceptos anteriores, en especial las colecciones. A partir de ahora tendremos subcolecciones, que en realidad son una parte de la memoria, indicada por dos punteros, inicio y fin, se expresará así:

si Mem(fh) es una matriz boleana

sub(mem(5h,8h))  es igual a una colección que va desde 5h hasta 8h. Por ejemplo:

si los valores contenidos en mem son éstos:

1 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | -esto viene a ser un stream o una colección de boleanos, que es casi lo mismo.

sub(mem(1h,4h)) devuelve 1110 porque son los valores de mem(4h), mem(3h), mem(2h) y mem(1h). lo puede devolver como colección de bits o como número
binario si así se indica.


Además, en este capitulo se empezará a usar el tipo texto. Es un tipo que representa un número de base 127. Si, lo oyes bien, el texto ahora es un número. Pero tranquilo, porque, normalmente, no sumaremos letras, sino que las concatenaremos. Para entendernos, utilizaremos el sistema ascii que viene a ser una tabla de símbolos. La unidad mínima de un texto es el char o carácter. un char es un número binario de ocho cifras, o uno hexadecimal de dos, que curioso, eh... pues no. En realidad se trata de otro ajuste de interface, ya que en binario, el último byte siempre es cero, pero una vez más es un ajuste adecuado por cuestiones de facilidad de uso.

Tambien comentar sobre el ascii, que por ejemplo, algunos sistemas como los que ejecutan Prim-os marcan siempre el octavo bit a 1, pero igualmente éste es ignorado en su interpretación. Y entender que un número es un número, y un texto que contiene un número es un texto. es decir:

1 <> "1"              -esto <> significa NOT=, y las comillas indican que 1 es texto ascii. ¿porque no es igual?

Bueno, en ascii los caracteres que significan números se representan por su valor en el primer nibble y la cadena 0011 en el segundo. por esto, para interpretar un número en ascii haremos lo siguiente:

creamos mem(8h) como boleano     -creamos una memoria de boleanos de dos nibbles, o un byte que es lo mismo.

mem()=stream("1") -declaramos que la memoria es igual al stream del texto "1", es decir, lo que significa en cadena boleana.

si miramos en la memoria:
mem():
mem(0h)=1
mem(1h)=0
mem(2h)=0 -el primer nibble indica el valor como si fuera un número binario de cuatro cifras.
mem(3h)=0

mem(4h)=1
mem(5h)=1
mem(6h)=0 -el segundo indica el valor 11, en decimal 3. Es por convención, para identificar que es un número en modo texto.
mem(7h)=0

así que el valor en número es igual al valor del primer nibble. creamos Bin(), que es una colección boleana:

bin()=sub(mem(0h,3h))
y para pasarlo a binario hacemos:

bin(&) a binario
y bin=0001 , el valor del número-char.

Así que siempre que trasformemos texto a binario haremos esto mismo, o bueno, lo hará la máquina. Todos los caracteres que no empiecen por 0011 serán ignorados, porque no son números.

Ya sabemos transformar entre las diferentes clases de datos, pero en realidad falta aclarar lo más importante, ¿qué es exáctamente un tipo de datos? Pues a nivel matemático es un casillero de un tamaño adecuado para su tipo de entrada, es decir, si entendemos que el único tipo de datos puro que existe es el boleano,del que deducimos el binario:
-utilizaremos una memoria principal, matriz boleana.

creamos mem(ffffh) como boleano -para el ejemplo vamos a crear un dato de cada tipo especificado utilizando sub, explicado arriba.
-además lo harémos sin sobreescribir ningúno, dándoles la memoria que necesitan.

un carácter hexadecimal = sub(mem(0h,4h)) -porque necesita cuatro bites para existir
un carácter ascii = sub(mem(4h,ch)) -este es un bite exáctamente, o sea lo necesario para dos hexadecimales o un char.
un carácter binario = mem(ch) -no hay que hacer sub, porque es sólo una cifra binaria, que ocupa lo mismo que el boleano.
un número entero16= sub(mem(dh,1fh)) -los números tambien tienen límite, se explica a continuación.

Y la verdad es que los tipos de datos es el primer paso para "subir" en el diseño por capas de los ordenadores actuales. Los números enteros también tienen unos límites, según la cantidad de bits que se utilizarán para crearlos, o sea, la distancia entre el puntero de inicio y el puntero del final. En este texto, contemplaremos todos los números como números matemáticos no limitados excepto si se declara lo contrario, o se llama, en lugar de número entero, número entero16,o número entero32 (indicando límite de 16bits o 32bits).

Para crear propiamente un valor de un tipo, hay que hacerlo en una memoria principal, en caso contrario, ésta no existe para la máquina, y se trata de un valor puramente matemático o de apollo.

Sub(mem(0,16))=new enteroA como entero   -significa que en la posicion de 0 a 16 se crea un nuevo valor llamado enteroA.
Sub(mem(0,1))=new interruptor como boleano -sí creamos un bit para usar como interruptor.

Lo interesante de esto es que existen diferentes formas para modificar un dato creado, aunque normalmente utilizaremos la asignación por el nombre de variable, o sea, nombredevariable=valor. Pero se puede hacer tambien así:

Sub(mem(0,15))=new ENT como número entero -creamos un número vacío que ocupa 16 bits en mem, desde el 0.
mem(0005h)=1 -modificamos añadiendo 1 en el puntero 5, es decir 32,
mem(0001h)=1 -modificamos añadiendo 1 en el puntero 1, es decir 2,
ENT=100010 -el número contenido ahora es el siguiente 100010, en decimal 34.

Todo esto nos sirve de base para entender qué es exáctamente una clase cuando hablamos de programación. Solo queda decir que en ocasiones pueden ser valores no modificables por el propio programa una vez declarados, éstos se llaman constantes en lugar de variables. Podemos observar que cualquier valor se puede traducir a una matriz boleana del tamaño adecuado para el tipo:

entero16=boleanos(16) -el tipo es similar a una matriz de 16 bits
char=boleanos(8) -en este caso de 8 bits

Y esto es muy útil, lo utilizará normalmente la máquina para ser capaz de trasformar, por ejemplo, números a texto así como stream() a cualquier tipo que se necesite. Lo interesante de usar tipos en lugar de matrices dimensionales de bits es que se evitan errores, como sumar una letra a otra letra, lo cual,posiblemente no devuelva una letra. Si el tipo está debidamente definido nuestro algoritmo efectuará las operaciones de forma adecuada. Si queremos, por ejemplo,encriptar un texto, si pudieramos usar sin problemas una matriz binaria interpretada como número binario o hexadecimal para hacer operaciones sobre estos valores y hacerlos ilegibles. Con el adecuado algoritmo devolvemos el valor a su forma inicial.

Entendiendo la importancia y la razón de ser de los tipos debemos utilizar sus propiedades en nuestro favor. Por ejemplo, limitando el tamaño delas variables al que vamos a utilizar, minimizando el desaprobechamiento de la memoria. Si trabajamos con un número que no puede pasar de 10, usaremos un número binario de cuatro cifras, un nibble... que es equivalente a un hexadecimal, la pérdida de datos será de 5 en decimal.

Cada lenguaje de programación de nivel medio-alto tiene su propia forma de interpretar las clases, pero suelen existir puntos en común. Este Texto no pretende enseñar uno o otro lenguaje (por ahora) así que se tratarán los tipos más comunes, como ya se ha dicho antes:

boleano-
binario-
entero-
hex-
hexadecimal-
char-
texto-

Normalmente hay que definir el tipo de cualquier variable, aunque algunos lenguajes utilizan un tipo genérico, en visual basic éste tipo se llama variant,y cualquier variable que no se defina en tipo se entiende que es variant. En nuestro lenguaje simplificado no usaremos variant nunca, sino matrices dimensionales de boleanos, que gracias a stream, split y (&) veremos que funciona igual.

Estamos preparados para diseñar pequeñas fórmulas para interrelaccionar los tipos conocidos:
Por ejemplo, vamos a enmascarar números para que en la memoria aparezcan como letras, y luego calcularemos cómo recuperar, o descifrar los números.

Vamos a enmascarar un número int16, lo cual ocupa cuatro nibbles, o dos bytes que es lo mismo. Así que para expresarlo en texto necesitamos dos caracteres en ascii. Pero hay un problema de diseño, ya que muchas de las combinaciónes no devuelven un texto ascii legible si sobreescribimos los dos nibbles en una letra ascii. Ésas letras no podrían ser accedidas o recuperadas como texto normal.

Lo que deberíamos hacer, es tirar de "perdida de memoria" para que funcione el sistema. De todas formas, la pérdida generada es un poco más que en una cadena de texto, incluso. Pero nos aseguramos de que al meter un número nos devuelve una letra,y al traducir ésta letra, viceversa.

Estudiando un poco la tabla ascii, especialmente las peculiaridades de los números, nos damos cuenta de que usamos dos nibbles para guardarlos, pero la primera parte siempre es igual:

Letra "0": 0011, 0000 = 3, 0

Letra "1": 0011, 0001 = 3, 1

Letra "2": 0011, 0010 = 3, 2

Letra "3": 0011, 0011 = 3, 3

La primera mitad de la derecha indica el valor del número en ascii, y la segunda siempre 3. Bueno, siempre que se trate de un número de 0 a 9 (bueno, y otros símbolos tambien, pero no los usaremos). Esto es interesante, ¿que pasa si cambiamos ése número 3 que lleva el paquetito ascii?. vamos a probar por separado.

creamos bin(8) que es una matriz boleana
Letra que es un char

letra="5" -letra ahora contiene un 5 ascii.

split(letra) a steam(bin()) -metemos letra a bin en modo stream de bits para que guardemos el valor boleano.

ahora mismo bin() contiene esto:

bin(0)=1
bin(1)=0    -por un lado, el número 5.
bin(2)=0
bin(3)=1

bin(4)=1
bin(5)=1
bin(6)=0    -por otro el número 3, que indica que es un numero en ascii.
bin(7)=0

entonces vamos a sobreescribir esa "cabecera" que siempre contiene el número 3. y vamos a hacerlo eligiendo una cabecera que exísta y signifique algo para todos los números del 0 al 9. Ahora mismo tengo a mano una tabla de símbolos ascii. Una posible elección es, por ejemplo, 0111 para letras comúnes en minúsculas o 0101 para la misma letra en mayúsculas. Usaremos la 0111, ya que se consigue sólo añadiendo una interrupcion en bin(6), o sea, haciendo esto:

bin(6)=1

y si miramos ahora el valor de bin():


bin(0)=1
bin(1)=0    -por un lado, el número 5.
bin(2)=0
bin(3)=1

bin(4)=1
bin(5)=1
bin(6)=1    -por otro el número 7, que indica que es una letra mayúscula.
bin(7)=0

entonces, ya no se trata de una letra "5" sino de una letra "U". Si probamos esto:

letra=sub(bin(0,7)) como char

ya lo tenemos: letra= "U"

Es un sistema rudimentario para cambiar de número ascii a letra de la P a la Y. Pero tambien, éste mismo sistema nos sirve para hacer una letra mayúscula:

si bin() es una matriz boleana que contiene "a"

stream(bin()) nos da esto: 0100 0001
y para hacerla mayúscula hay que cambiar el último 0100 por 0110 añadiendo en bin(5) un uno.

bin(5)=1
ahora nos da "A"

Y para hacerlo con otros caracteres es igual, es decir bin(5)=1, aunque no empiece por 0100, ya que el
ascii tambien está simplificado y por eso tiene cierta lógica o paralelísmo entre a y A, b y B y así
sucesivamente.

Estos son sistemas deficientes porque se tratan de una fórmula lineal que no tiene en cuenta ciertos factores para evitar errores. Por ejemplo, que pasaría si bin(5) ¿contiene ya un uno? pues que el char no cambia, pero procesamos ese cambio, sustituimos un uno por un uno, lo que es desperdiciar tiempo. Puede que no se note apenas, pero suponte que cada una de las letras ascii que están en tu ram, generara un proceso de éste tipo nada más entrar en ella. Igual en un ordenador viejo, o una pocket pc notaríamos algo de retardo.

Recuerda que es sólo teoría. En la práctica, el nivel bajo del ordenador está diseñado de un modo que puede evitar hacer ciertas operaciones inútiles, así como obviar ciertas cosas (no tienes que escribir en binario, pero está claro que en la memoria, el número entra en binario). Además estámos tratando el proceso informático desde un punto de vista de pequeñas memorias y procesos de nivel matemático, por lo que, mas o menos, se podrían llevar a cabo en pequeños chips electrónicos, más que en los grandes pcs. Poco a poco nos acercaremos a los procesadores grandes, que contienen diferentes "casilleros de memoria" que conforman lo que se llama arquitectura.

Pero esto es un tutorial diferente, ya que no vamos a enfocar, al principio, en ninguna tecnología específica, sino en conceptos matemáticos, o teóricos que se pueden aplicar a cualquier sistema que pueda contener y manejar memorias. La idea es explicar cómo un lenguaje, está formado por capas de diferente nivel de abstracción, pero todo empieza en una matemática muy muy basica. Cuando empieces a programar, posiblemente lo hagas en un nivel alto, pero a pesar de ello verás que trabajas con los mismos tipos, enteros, enteros largos, caracteres, strings (texto), incluso a veces tambien colecciones y colecciones de colecciones, solapadas en una estructura de objetos abstractos.

El problema es que si buscas en internet cómo programar, te enseñan a programar en uno o otro lenguaje, así aprendes tambien los vicios propios de ese mismo lenguaje y puede ser dificil deshacerte de ellos. Pero una vez se entiende cómo un procesador crea, mueve y trasforma datos en la memoria, se tiene otra visión de la programación. Poco a poco verás que los lenguajes de programación tienen paralelísmos y puntos en común, y que al fin y al cabo todo se reduce a mover datos en matrices binarias o submatrices (variables etc). Verás que los lenguajes de bajo nivel, como son el assembler utilizan además del propio procesador, partes del sistema operativo, que se encuentran en la memoria ram lo que ya no es un nivel tan bajo.

Pero por ahora sólo sabemos que un procesador hace cálculos y que una memoria guarda interruptores, ademas de crear tipos de datos, o casilleros y escribir en ellos. Aún falta entender un par de conceptos básicos para poder escribir un algoritmo. El sistema matemático está muy bien para expresar comportamientos en pequeños chips pero poco a poco adoptaremos expresiones del pseudocódigo, las cuales explicare paso a paso.

No hay comentarios:

Publicar un comentario