jueves, 5 de septiembre de 2024

Programación del Sinclair QL (XXV): Arboles. Pruebas de los nodos


Índice de entradas sobre programación del Sinclair QL



Vamos con el programa para poder probar lo escrito en la entrada anterior. Es muy sencillo, genero en un bucle una cantidad aleatoria de nodos, cada uno con un valor que será una cadena de caracteres aleatorios entre "A" y "Z". Luego pruebo las rutinas de modificar los punteros a los hijos de los nodos, y la de cambiar el contenido del nodo.

Como las rutinas para montar los nodos están en otro fichero, tengo que cargarlo con un MERGE. En las entradas anteriores usaba un módulo base con los MERGE, y si se ejecutaba el módulo más de una vez volvía a ejecutarlos, con la pérdida de tiempo que eso genera.

La solución ha sido añadir un controlador de errores, si detecta que no se ha cargado el módulo hace el MERGE, si ve que se ha cargado no hace nada. De esta forma podemos ejecutarlo varias veces sin pérdidas de tiempo.

  • WHEN ERRor: Es la rutina que se encarga de errores, para activarla se mira en la línea 260 el valor de una variable NODOS, si no existe dará un error -17
  • Prepara el sistema: Prepara la pantalla y llama a la rutina del módulo de nodos para inicializarlo
  • Crea módulos: Crea los nodos con información aleatoria
  • Modifica módulo: Modifica un nodo para añadir puntero a los hijos y cambia el contenido.
  • RndChr: Retorna un caracter aleatorio entre "A" y "Z"


100 REMark *************************************************************
110 REMark * Pruebas para el Manejo de nodos del arbol binario
120 REMark *************************************************************
130 :
140 REMark --- Control de si se ha incluido o no el módulo de nodos
150 :
160 WHEN ERRor
170   IF ERNUM=-17 THEN
180     PRINT "Cargando rutinas de nodos..."
190     MERGE mdv1_nodos
200     NODOS=1
201     RETRY
210   ELSE
220     PRINT "Se ha producido un error ";ERNUM;" en l“nea ";ERLIN
230     STOP
240   END IF
250 END WHEN
260 IF NODOS=0 THEN REMark --- Si da un error es que no ha cargado ---
270 :
280 REMark --- Preparar el sistema
290 :
300 MODE 0 : PAPER 0 : CLS
310 inicializa
320 :
330 REMark --- Generar nodos aleatorios para probar las rutinas
340 :
350 generar = RND(1 TO 10) : PRINT "Generando "& generar & " nodos"
360 FOR n=1 TO generar
370   c$=""
380   IF RND(1 TO 9) > 2 THEN
390     FOR i=0 TO RND(20)
400       c$ = c$ & RndChr$
410     NEXT i
420   END IF
430   n$ = NuevoNodo$(n,c$)
440   PRINT n$
450 NEXT n
460 :
470 PRINT "Finalizados los ";n;" nodos"
480 PRINT
490 PRINT "Cambiando datos de un nodo"
500 PRINT NuevoHijo$(n$,pIzq,56)
510 PRINT NuevoHijo$(n$,pDer,67)
520 PRINT NuevoValor$(n$,"Pruebas de valores")
530 :
540 REMark --- Retorna un caracter aleatório entre A y Z
550 :
560 DEFine FuNction RndChr$
570   RETurn CHR$(RND(CODE("A") TO CODE("Z")))
580 END DEFine aleatorio
590 :

 

El programa fuente lo podéis bajar de aquí.


En la siguiente entrada se añadirá el módulo memoria, añadiendo y eliminando nodos en la misma, por lo que luego mejoraré este programa para poder hacer pruebas de ambas partes, añadiendo un menú para definir cual se desea probar.

Para completar habría que hacer un proceso para compactar la memoria, pero eso requiere cambiar los punteros de los nodos, para lo que es necesario conocer su estructura interna, cosa que el sistema operativo no puede hacer, es responsabilidad de nuestro programa. En los lenguajes modernos con recolectores de basura, como Java o C# intentan hacer esto, con la ventaja de que no usan punteros a memoria, sino que las referencias son a la tabla de variables, con lo cual al cambiar la tabla ya ha cambiado todos los punteros, lo que es mucho mas sencillo que ir buscando en cada objeto a donde apunta y cambiarlo, pero a cambio usan mas memoria para la tabla de variables.


miércoles, 4 de septiembre de 2024

Programación del Sinclair QL (XXIV): Arboles. Estructura de los nodos en Super Basic

 

Índice de entradas sobre programación del Sinclair QL



Hemos visto el tema de las variables y el de los punteros, ahora vamos a hacer algo en Super Basic, pero ya que no es un lenguaje que soporte punteros ni se pueden usar variables compuestas de tipo registro, es un poco más complicado su manejo. Para manejar memoria hay que usar llamadas al Sistema Operativo para reservar memoria y código máquina para manejar los registros y punteros, o en el Toolkit II hay algunas funciones que nos lo facilitan. Como esto se sale de estas entradas que son de programación en SuperBasic, con la idea de que se pueda portar a otros Basic, voy a hacer otra cosas diferente para manejar árboles binarios, aunque no descarto luego hacer algo usando código máquina y llamadas al sistema.

Simularé la memoria usando una cadena de caracteres, usando el hecho de que una cadena tiene n caracteres, y podemos acceder a cualquiera de ellos individualmente, y cada carácter tiene un valor de 8 bits, lo que es muy similar a como se maneja una memoria. Desarrollaré funciones para reservar "memoria" dentro de la cadena, guardar datos en ella, leer cualquier dato usando un puntero a su dirección base, etc.

Nodos

Para crear los nodos del árbol, en entradas anteriores usé varios vectores de enteros, ya que los valores que manejamos lo eran. Ahora usaremos valores de tipo cadena de longitud variable, por lo que ya no nos sirve usar un vector, desperdiciamos demasiada memoria definiendo cadenas de longitud fija que pueden estar casi vacías. En su lugar en C se usaría un registro, que es una variable que se subdivide en varias, comportándose como un todo o como cada variable individual. Veamos como lo definiríamos en C:

struct nombre_registro    //El nombre que deseamos darle
{
     tipodato campo_1;    //Se define como cualquier variable, tipo y nombre
     tipodato campo_2;
     ....
     tipodato campo_N;    //Podemos usar todas las variables necesarias       
};

En el programa usaremos el nombre como el tipo de variable:

nombre_registro Variable_Registro;     //El nombre que deseamos darle a la variable

Y luego llamamos a los valores añadiendo un punto al nombre:

Variable_Registro.campo_n = ....
if (Variable_Registro.campo_n == true) {....

En los árboles binarios, necesitamos estos datos dentro para montar el registro:

  • Nodo_Izquierdo: Puntero al sub-nodo izquierdo de este nodo.
  • Nodo_Derecho: Puntero al sub-nodo derecho de este nodo.
  • Nodo_Padre: Puntero al nodo padre de este nodo.Este dato no es estrictamente necesario, pero simplifica el montaje del árbol.
  • Contenido del nodo: En nuestro caso es una cadena de caracteres, pero puede ser cualquier tipo, como un entero o un vector, o incluso otra estructura de tipo registro.
  • Longitud del contenido: Al ser una cadena de longitud variable, necesitamos sabes su longitud para reservar solo la memoria necesaria.

Funciones para montar registros

Para montar un registro usaré una cadena de caracteres dividida en partes, los punteros serán valores numéricos entre 0 y 99, mientras que el contenido lo limitaremos a entre 0 y 99 caracteres. 

Para almacenar los números usaré cadenas de longitud fija, ajustadas a la izquierda con ceros por la derecha, así será sencillo ver su valor, realmente lo que se debe usar son bytes, con 1 byte podemos almacenar un número entero sin signo entre 0 y 255, con 2 bytes alcanzamos entre 0 y 65.535, esto pueden ser fácilmente dos caracteres, pero muchos no sería visibles ni nos informarían de los valores del puntero, por eso prefiero usar números almacenados en la cadena, aunque solo puedo almacenar entre 0 y 99 tengo la ventaja de que los puedo ver de forma sencilla, y este desarrollo intenta ser instructivo más que efectivo.

Nuestro nodos serán una cadena de caracteres con este formato <NN:NN:NN:NN:cadena> en donde:

  • Inicio del nodo: Un carácter "<"
  • Puntero al nodo padre: Dos caracteres numéricos. No es necesario pero simplifica la navegación por el árbol.
  • Separador: Un carácter ":"
  • Puntero al nodo hijo izquierdo: Dos caracteres numéricos.
  • Separador: Un carácter ":"
  • Puntero al nodo hijo derecho: Dos caracteres numéricos.
  • Separador: Un carácter ":"
  • Longitud del contenido: Dos caracteres numéricos.
  • Separador: Un carácter ":"
  • Contenido: Una cadena de entre 0 y 99 caracteres
  • Fin del registro: Un carácter ">"

Los caracteres de inicio y fin no son necesarios, así como los separadores tampoco lo son, pero los usaré para poder ver los nodos de manera sencilla, y rellenaré la "memoria" con asteriscos, de forma que se vean los huecos libres, y cuando queramos ver la "memoria" lo podemos hacer simplemente imprimiendo la cadena, y veremos un texto de la forma:

<00:01:23:04:HOLA>******************<19:40:00:05;ADIOS><04:25_09:00:>********....

Así creo que es mas sencillo presentar los valores de forma que se vean correctamente, sin necesidad de grandes procesos.

Empecemos por las funciones que montan los nodos, para lo que defino funciones y procedimientos para manejarlas:

  • inicializa: Este procedimiento inicializa las variables que usaremos, en donde:
    • mLon: Longitud de las cadenas para guardar los punteros
    • mNodos: Máximo número de nodos que manejamos
    • nIni, nSep, nFin: inicio, separador y final para montar el nodo
    • pPadre, pIzq, pDer, pLon, pVal: Orden de los campos para el nodo
  • n2c$(n,l): Esta función retorna una cadena cuyo contenido es un valor numérico (n), ajustada a una longitud máxima (l) y rellena con ceros por la izquierda. Si le pasamos por ejemplo (17,3) nos retorna "017"
  • MontarNodo$(p,i,d,c$): Función que retorna un nodo montado con los valores para el padre (p), hijo izquierdao (i), hijo derecho (d) y una cadena de caracteres (c$), si le pasamos por ejemplo (2,12,3,"Hola") nos retorna "<02:12:03:04:Hola>"
  • NuevoNodo$(p,c$): Función que retorna un nuevo nodo sin hijos, con los valores para el padre (p) y una cadena de caracteres (c$), si le pasamos por ejemplo (2,"Hola") nos retorna "<02:00:00:04:Hola>"
  • Posicion(pos): Retorna un número que indica donde comienza en la cadena que representa el nodo uno de los valores del nodo (pos con valores para 0=padre, 1=izquierdo, 2=derecho, 3=longitud del contenido, 4=valor del contenido), si le pasamos por ejemplo (pIzq) nos retorna 5
  • NuevoHijo$(nodo$, pos, valor): Retorna una cadena que representa un nodo (nodo$), en la que cambia el puntero para uno de los hijos (pos donde 1=izquierdo o 2=derecho), con su nuevo valor de puntero (valor), si le pasamos por ejemplo ("<02:00:00:04:Hola>", pIzq,2) nos retorna "<02:02:00:04:Hola>"
  • NuevoValor$(nodo$, NuevoValor$): Retorna una cadena que representa un nodo (nodo$), en la que cambia el valor del contenido (NuevoValor$), si le pasamos por ejemplo ("<02:02:00:04:Hola>", "Adios") nos retorna "<02:02:00:05:Adios>"

5000 REMark *************************************************************
5010 REMark * Librería.: Arbol Binario                                  *
5020 REMark * Programa.: Nodos                                          *
5030 REMark * Contenido: Rutinas de manejo de los nodos                 *
5040 REMark * Autor....: javu61, octubre 2024                           *
5050 REMark *************************************************************
5060 :
5070 REMark --- Inicializar el sistema
5080 :
5090 DEFine PROCedure inicializa
5100   mLon=2
5110   mNodos = 10^mLon - 1
5120   nIni$="<" : nSep$=":" : nFin$=">"
5130   pPadre=0 : pIzq=1 : pDer = 2 : pLon=3 : pVal=4
5140 END DEFine inicializa
5150 :
5160 REMark --- Numero en cadena ajustado a longitud
5170 :
5180 DEFine FuNction n2c$(n,l)
5190   LOCal c$
5200   c$=n
5210   REPeat bucle_n2c
5220     IF LEN(c$)>=l THEN EXIT bucle_n2c
5230     c$ = "0" & c$
5240   END REPeat bucle_n2c
5250   c$= c$(1 TO l)
5260   RETurn c$
5270 END DEFine n2c$
5280 :
5290 REMark --- Montar un nodo
5300 :
5310 DEFine FuNction MontarNodo$(p,i,d,c$)
5320   LOCal n$
5330   n$ = nIni$
5340   n$ = n$ & n2c$(p,mLon) & nSep$
5350   n$ = n$ & n2c$(i,mLon) & nSep$
5360   n$ = n$ & n2c$(d,mLon) & nSep$
5370   n$ = n$ & n2c$(LEN(c$),mLon) & nSep$ & c$
5380   n$ = n$ & nFin$
5390   RETurn n$
5400 END DEFine MontarNodo
5410 :
5420 REMark --- Montar un nodo nuevo sin hijos
5430 :
5440 DEFine FuNction NuevoNodo$(p,c$)
5450   RETurn MontarNodo$(p, 0, 0, c$)
5460 END DEFine NuevoNodo
5470 :
5480 REMark --- Retorna la posición de un elemento en el nodo
5490 :
5500 DEFine FuNction Posicion(pos)
5510   RETurn LEN(nIni$) + (pos * mLon) + (pos * LEN(nSep$)) + 1
5520 END DEFine Posicion
5530 :
5540 REMark --- Añadir un hijo a un nodo
5550 :
5560 DEFine FuNction NuevoHijo$(nodo$, pos, valor)
5570   LOCal p
5580   p = Posicion(pos)
5590   nodo$(p TO p+mLon-1)= n2c$(valor, mLon)
5600   RETurn nodo$
5610 END DEFine NuevoHijo$
5620 :
5630 REMark --- Cambiar el valor de un nodo
5640 :
5650 DEFine FuNction NuevoValor$(nodo$, NuevoValor$)
5660   LOCal p1,p2
5670   p1 = Posicion(pLon)
5680   p2 = Posicion(pVal)
5690   nodo$ = nodo$(1 TO p1-1)
5700   nodo$ = nodo$& n2c$(LEN(NuevoValor$), mLon) & nSep$
5710   nodo$ = nodo$ & NuevoValor$ & nFin$
5720   RETurn nodo$
5730 END DEFine NuevoValor$

 

En la siguiente entrada añadiré un programa para probar que estas funciones trabajen correctamente, y si queréis descargar este fichero lo tenéis aquí.

miércoles, 7 de agosto de 2024

Programación del Sinclair QL (XXIII): Punteros

Índice de entradas sobre programación del Sinclair QL



Tras la entrada sobre las variables, entraremos a explicar lo que son los punteros, ya que la definición habitual es real pero muy poco explicativa: Un puntero es una variable que apunta a otra variable.

En la siguiente entrada usaremos ya los punteros en SuperBasic, que aunque no los soporta de forma nativa, hay maneras de simularlos.

Punteros

Como vimos en la entrada anterior, los compiladores e intérpretes usan una tabla en donde almacenar los datos de las variables que se utilizan en el programa. Las entradas de esta tabla disponen de una serie de datos importantes: nombre, tipo, longitud y posición en memoria donde se almacena (lo que se denomina dirección base de la misma, y su ocupación es la longitud de la misma (normalmente viene dados por el tipo).

Un puntero es una variable de tipo entero, que almacena una dirección de memoria, usualmente la dirección base de otra variable o de una zona de memoria libre. Veamos los dos tipos con ejemplos en  lenguaje C (lo tengo bastantes oxidado, quizá no sea del todo correcta la sintaxis): 


[1] float a=78.65;
[2] float *b=&a;
[3] float *c=malloc(4);
[4] printf('%d %f', b, *b);
[5] printf('%d %f', c, *c);
[6] *c = a;
[7] printf('%f', c);


  • En la línea 1 creamos una variable de tipo decimal que ocupa 4 octetos, y le damos como valor inicial 78'65. 
  • En la línea 2 creamos una variable de tipo puntero, que apuntará a una zona de memoria que contiene una variable de tipo decimal de 4 octetos, y la inicializamos con la dirección de base de la variable a. 
  • En la línea 3 creamos una variable de tipo puntero que apuntará a una variable de tipo decimal, y lo inicializamos a una nueva zona de memoria en donde se reservan 4 octetos.
  • En la línea 4 el sistema imprimirá la dirección de memoria a la que apunta la variable b, seguida por el contenido de la memoria a la que apunta b, en este caso 78'65.
  • En la línea 5 el sistema imprimirá la dirección de memoria a la que apunta la variable c, seguida del contenido de esa dirección de memoria, en este caso indefinido pues no se ha inicializado a ningún valor, puede ser cero o dar un error por no servir el contenido como representación de una variable decimal.
  • En la línea 6 asociamos a la dirección base apuntada por la variable c el contenido de la variable a, sería lo mismo que hacer un c=a si ambas variables fueran float.
  • En la línea 7 se imprime 78'65

Como vemos, si una variable de tipo puntero la usamos por su nombre, obtenemos la dirección de memoria a la que apunta, pero si el nombre lo precedemos por asterisco, obtenemos el contenido de dicha dirección de memoria. Igualmente una variable precedida por & nos retorna al dirección de memoria de dicha variable y no su valor.

Uso de los punteros a variables

Al definirse las funciones, los parámetros que les pasamos son variables locales cuyo valor inicial es que que les pasemos en la llamada, y al finalizar  solo retornan un valor, pero en C definieron un sistema para poder alterar los parámetros  usando los punteros. Vemos un ejemplo:


[1] void incrementar(int i) { i = i + 1; }
[2] void duplicar(int *j) { j = j + 1; }
[3] int a=5; incrementar(a); printf('%c', a);
[4] int b=5; duplicar(&b); printf('%c', b);

  • En la línea 1 creamos una función que recibe un parámetro, que es una variable local a la misma, esto incrementa la variable en una unidad. En la línea 2 definimos una semejante, pero recibe un puntero como parámetro.
  • En la línea 3 definimos una variable entera con valor 5, llamamos a incrementar, al que le pasamos el valor de a que es 5, como en la línea 1 estamos incrementando la variable local no se afecta, y luego se imprime otra vez 5. 
  • En la línea 4 definimos una variable entera con valor 5, llamamos a duplicar, al que le pasamos la dirección de b, como en la línea 2 estamos recibiendo el puntero, al incrementar será la zona de memoria a la que apunte, por tanto al final de la línea imprimirá 6.

De esta manera es sencillo para el programador decidir que variables serán alterada en las funciones y cuales no, protegiendo mejor el programa. En lenguajes modernos sin punteros, se añade una palabra, normalmente var, antes de la variable para indicar que su valor podrá ser alterado, lo que internamente es usar un puntero de forma transparente.

Otro uso habitual es emplear punteros a funciones, de esta manera en una variable podemos guardar una u otra función en relación con los datos que estamos procesando, pudiendo alterar el manejo del programa sin necesidad de complicar mucho la lógica. 

En lenguajes modernos esto es transparente, por ejemplo un objeto es solo un puntero a una posición de memoria que contiene funciones y variables, lo manejamos sin necesidad de saber siquiera lo que es un puntero.

Uso de punteros a memoria

La principal ventaja de los punteros es el manejo de memoria libre de forma no estructurada y de longitud variable, pudiendo crear elementos libremente cuando los necesitemos, a diferencia de los vectores que debemos definir su número de elementos y el tamaño de estos antes de empezar a manejarlos. Esto es muy útil para manejar estructuras de datos, como una lista de cadenas, cuando necesitamos un elemento llamamos a una función que nos retorne una dirección de memoria libre, del tamaño que necesitemos para esa cadena más el puntero al siguiente elemento, guardamos los datos, ajustamos los punteros, y ya tenemos una lista que podemos recorrer, sin saber cuanto ocuparán las cadenas de caracteres ni cuantas usaremos. 

Esto lo hacen los lenguajes modernos de forma transparente, al crear una estructura de tipo lista, que luego podemos recorrer con un FOREACH.

Problemas de los punteros

Tras muchos años manejando punteros, por los problemas que pueden ocasionar su uso, en los lenguajes actuales se ha optado por eliminarlos, aunque el sistema los maneja de forma interna de forma transparente, en nuestro programa no podemos usarlos.

El principal problema de los punteros es que son propensos a usarse de forma errónea provocando errores en el programa o, lo que es peor, en el sistema. Si apunto mal en una lista, puedo crear una circular y no salir nunca del bucle que la recorra, o apuntar mal un elemento y salirnos de la lista, por lo que el programa trabajará con datos erróneos. En un vector no podemos usar mas elementos de los definidos, pero con punteros podemos pasarnos y seguir leyendo elementos incorrectos. Esto se soluciona siendo cuidadosos al programar y probar bien nuestros programas con muchos datos diferentes.

Otro problema es la fragmentación, si creamos y destruimos muchos trozos pequeños de memoria, el sistema no puede reutilizarlos y podemos ocupar toda la memoria posible sin darnos cuenta. Esto tiene difícil solución, habría que compactar la memoria reubicando los registros y los punteros, lo que puede ser largo. Esto es realmente lo que hacen en lenguajes modernos los recolectores de basura de forma automática y transparente, ocultando los punteros a los programadores.

Otro problema es la seguridad, mediante un puntero podemos acceder a cualquier posición de memoria, por tanto podemos leer en otras zonas de memoria que no son específicas de nuestro programa, en sistemas como UNIX que son multi-usuario, podemos leer la memoria asignada a otro usuario y obtener por ejemplo claves de acceso. Hay mecanismos en el sistema operativo para que esto no sea posible, dando errores si accedemos a ubicaciones de memoria que no son del rango que tenemos asignado a nuestro programa.

miércoles, 31 de julio de 2024

Programación del Sinclair QL (XXII): Variables

Ampliado el 04/09/2024 en azul y el 05/09/24 en rojo


Retomo tras varios años la serie de programación de mi querido QL, seguiremos con los árboles, pero antes hablemos de conceptos básicos sobre punteros, necesarios para entender como montar los árboles.

Aunque esta entrada entrá en la serie del QL, realmente es general sobre programación, debes entender estos conceptos ya que son válidos para cualquier lenguaje, y en la siguiente hablaré sobre punteros.

Compiladores e Intérpretes

Escribimos nuestros programas en algún lenguaje, en donde usamos variables como un sistema para almacenar los datos que manipulemos y la lógica que usamos para su manipulación. Para ejecutar el programa podemos usar dos sistemas:

  • En BASIC normalmente se usa un interprete, que va leyendo el programa fuente línea a línea y ejecutándola, esto es bastante simple y necesita poco espacio en memoria.
  • Los compiladores en cambio convierte el programa fuente en otro que reconozca la máquina (normalmente código máquina, pero en algunos como JAVA generan un código intermedio que luego se procesan usando un ejecutor o una máquina virtual). Para ello se usa un programa que va leyendo el programa y ejecutando el proceso en una o varias pasadas, montando una serie de tablas para poder manejar cosas que todavía no se han definido. Esto requiere mas memoria para disponer del compilador, del buffer donde almacenar el programa que vamos leyendo, las tablas auxiliares, etc.

Variables

En ambos casos se necesita reconocer las variables, de forma que siempre que encontremos una podamos usar su valor correcto. Para ello en ambos sistemas se crea una tabla de variables, en donde se almacenan los datos necesarios para manejarlas, lo que se denominan atributos. En esta tabla se incluyen varios atributos importantes:

  • Nombre: El nombre que le damos a una variables para nosotros es su principal atributo, ya que es con el que interactuamos con ellas. Su longitud es muy variable entre lenguajes, en los primeros BASIC constaba de una letra o de una letra y un solo dígito (A, B$, C2, D4 ...), en los TRS-80 su BASIC permitía nombre largos, pero solo consideraba los dos primeros (lo que solo es un poco mejor), y por experiencia personal se puede programar así solo requiere disciplina. En lenguajes antiguos se comenzaron usando hasta 6, luego se aumentó a 8 (bastantes años más tarde, y en las primeras versiones del ABAL, un dialecto del BASIC usado por BULL, se mantenían ilimitados pero solo consideraba los 8 primeros), rápidamente creció el tamaño hasta 256, y hoy día podemos decir que los lenguajes modernos tienen longitud ilimitada. Los nombres deben comenzar por una letra, o por el símbolo de subrayado más una letra. Hay lenguajes que distinguen mayúsculas y minúsculas en los nombre (lo que ocasiona errores si te equivocas, por lo que se recomienda usar siempre mayúsculas o minúsculas).
  • Tipo de Contenido: En los lenguajes antiguos teníamos variables numéricas de tipo entero o de tipo decimal, y variables alfanuméricas, de manera similar a lo que soportan los propios ordenadores internamente, luego se han ido ampliando los tipos:
    • Alfanuméricas: Almacenan caracteres alfabéticos, caracteres numéricos y símbolos especiales (espacio, punto, +, *, &, etc.). En general hay estos tipos:
      • Carácter o char: Un solo caracter
      • Cadena o string: Una lista de caracteres, que puede ser de longitud fija (tienen un máximo que no se puede sobrepasar, hay lenguajes que rellenan con espacios por la derecha y otros que no lo hacen, marcando el final de la cadena), o de longitud variable (la cadena ocupa su longitud al crearla, si se amplia su contenido se amplia la cadena a su vez).
En el QL las variables de cadena se definen añadiendo el símbolo $ al nombre de la variable, y almacenan valores de 8 bits en código ASCII, aunque dependiendo de la ROM usará una configuración regional diferentes para algunos caracteres locales, como la Ñ o la £.
    • Enteras: Un valor numérico sin decimales, son los más rápidos de manejo por lo que debe priorizarse su manejo. Internamente se guardan en binario, usando o bien un bit para el signo o bien usando complementos (esto lo explicaré mas adelante), su representación interna siempre es exacta, pero están limitados en rango por su tamaño, así en 8 bits y usando uno para el signo, disponemos de valores entre -127 a +127 (realmente -128 a 127, cosas del binario, pasará lo mismo con el resto de tamaños), con 16 bits ampliamos a ±32.767, con 32 bits alcanzamos ±2.147.483.647, con 64 bits ±9.223.372.036.854.775.808 (ojo, el punto es el separador de miles que usamos en España, en otros países serían comas). Realmente hay que
El tamaño del entero es el base del procesador, en máquinas antiguas serían 8 bits, hoy día es de 64, y se han definido muchas variantes:
      • Entero: Por defecto del tamaño de registro del procesador (8,16,32,64)
      • Entero sin signo: Si no necesitamos números negativos, con este tipo ampliamos en 1 bit el tamaño, por lo que con 8 bits serían de 0 a 256, con 16 bits de 0 a  65.536, etc.
      • Entero corto y largo: Cuando las máquinas eran de 8 bits no había otra opción, pero al ir ampliando el tamaño a 16, para reducir ocupación en memoria que era un recurso muy limitado siempre, se definieron enteros cortos que ocupaban la mitad, y enteros largos que ocupaban el doble.
      • Carácter: Representa el valor numérico de un carácter alfanumérico en el código ASCII, pero en ciertos lenguajes podemos usarlo como un entero corto de 8 bits (o 16 si es en Unicode), soportando también su uso con o sin signo. 
      • Booleanas: Un entero en donde 0=Falso y 1=Cierto, o más generalmente cero falso y diferente a cero cierto. Ocupan un byte completo para solo usar un bit, lo que en máquinas de 64 bits es mucho espacio desaprovechado.
      • Fecha, hora, fecha y hora: Son valores enteros que guardan realmente el número de días desde una fecha inicial dada, la hora del día en décimas de segundo, o ambos simultáneamente en dos enteros.
      • Punteros: Esto lo veremos luego.
      • De coma fija: Estos son números que tienen una cantidad de decimales predefinida, y su valor se guarda directamente en binario como un entero, redondeando el valor a los decimales que aceptan, ajustando los decimales truncando o añadiendo ceros por la derecha para alcanzar los definidos, y luego eliminando el punto decimal, así serán enteros y de esta manera se pueden sumar directamente entre sí, añadiendo ceros por la derecha si es necesario igualar el número de los decimales entre las variables (esto es muy rápido en los ordenadores).
En el QL las variables enteras se definen añadiendo el símbolo % al nombre de la variable, y almacenan valores en 16 bits, entre -32.768 y +32.767.
    • Decimales: Un valor numérico que admite decimales. Internamente se guarda con una codificación científica, del tipo smEn, donde s es el signo, m la mantisa (se guarda el valor completo como todos sus dígitos sin punto decimal), y n sería el exponente (donde se ubica el signo decimal). Hay varios tipos según su tamaño, lo que cambia es la cantidad de cifras significativas de la mantisa y del exponente.:
      • La codificación normalizada IEEE 754 ocupa 32 bits, denominándose de simple precisión
      • Las de media precisión ocupan 16 bits
      • Las de doble precisión ocupan 64 bits
      • Las de cuádruple precisión o doble precisión largas, con 128 bits
      • Hay hasta las de octuple precisión con 256 bits.
En el QL las variables decimales se definen sin añadir nada a su nombre, y almacenan valores en 16 bits, su valor se presentará como un número si no es excesivamente grande, o con formato científico en caso contrario. 
    • Tipos especiales: Aquí cada lenguaje tiene sus variantes, podemos hablar en general de:
      • Puntero:  que guarda una posición de memoria, es el objetivo de esta entrada
      • Tipo indefinido (void): representa que no usaremos su valor por lo que solo nos interesa su nombre (en C una función se comporta como una variable, ya que dispone de su entrada en la tabla de Variables, las funciones son las que retornan un valor, los procedimientos no las retornan y se definen de tipo void)
      • Cualquier tipo (any): que indica que guardaremos cualquier posible valor en la variable (usar con cuidado ya que ralentiza mucho el programa). Son usuales en JavaScript.
      • Objeto: realmente son punteros, pero que apuntan a una estructura en memoria compleja que dispone de código y datos.
El tipo de contenido representa un tamaño en la memoria. Hay lenguajes fuertemente tipados, que no permiten asignar una variable de un tipo a una de otro tipo, y lenguajes pobremente tipados que hacen conversiones automáticas entre los tipos de las variables (esto por si solo requiere una entrada para ello, ya veremos si algún día la hago). NOTA: Nuestro QL es pobremente tipado, por tanto podemos asignar a una numérica una cadena, e intentará hacer la conversión a partir de su contenido. Si escribís a$="12.45 MAS" : b=a$ : c%=a$ : PRINT a$, b, c% veréis que en pantalla aparece   12.45 MAS       12.45       12

  • Ubicación en memoria: Todos los datos deben estar ubicados en algún lugar de la memoria del ordenador, en general se reserva una zona denominada "pila del programa" o de forma similar, en donde se guardan todas las variables que se utilicen. Para variables simples apuntará directamente a donde está su valor, pero para variables mas complejas apunta al inicio de la zona en que está ubicada. Para crear la entrada para un nuevo valor, se mira la posición de la última entrada y se le suma la ocupación, así tenemos la ubicación de la nueva variable. Si fuera la primera variable y por tanto la tabla está vacía, se rellena con la dirección base de la "pila del programa".
Desde nuestro programa no vemos esa tabla de variables ni sabemos nada de direcciones de memoria, realmente solo nos interesa conocer el VALOR que es el contenido de la variable, realmente unas posiciones concretas en la memoria apuntadas por la tabla anterior. En la mayoría de lenguajes su valor se inicializa al comenzar el programa a un valor por defecto, cero para numéricas (en booleanas cero equivale a false, en fechas el valor sería 0, pero como representa los días que transcurren desde una fecha base preestablecida sería el valor de esa fecha), las cadenas se inicializan como vacías o con espacios según el lenguaje.
 
Hay que tener cuidado con lenguajes donde las variables no se inicializan como C o C++ , tendrán un valor aleatorio según lo que haya en ese momento en la memoria, por lo que se siempre se recomienda darles el valor inicial al definirlas en la forma int a=0; float b=0.0; char Saludo[5]="Hola";
 
En nuestro QL el sistema utiliza tres listas separadas para el manejo de las variables, procedimientos y sus nombres:
  • Por un lado tenemos la lista de nombres de las variables, procedimientos y funciones. La primera vez que las definimos se guarda en esta lista, y a partir de entonces al escribir su nombre el sistema lo ajustará para que coincida con lo que has escrito en cuanto a mayúsculas y minúsculas en las líneas. Esto hace que si queremos cambiar la variable nododerecho por NodoDerecho el sistema no nos deje, pues ya está en la lista.
  • Otra lista contiene los tipos de cada variable, procedimiento o función, para las locales incluye el procedimiento o función en que tienen vigencia, junto al puntero en la memoria donde está ubicado su valor.
  • Una tercera es la que contiene los valores de las variables.

Definición explicita de variables

En BASIC o Python no es necesario definir las variables antes de su uso, son lo que se denomina lenguajes implícitos, conforme avanza el programa se va rellenando la tabla de variables cuando se localiza una nueva, esto puede ocasionar problemas ya que si nos equivocamos en el nombre, el sistema crea una nueva variable sin que nos demos cuenta, lo que provocará un error que habitualmente cuesta localizar.
 
En contra la mayoría de lenguajes son de tipo explícito, obligan a definir una variable antes de poder usarla, algunos como en COBOL separadas del código ejecutable, otros como Pascal C (y los que usan su sintaxis como C++, C#, Java o PHP) se pueden definir en cualquier momento. Al definir las variables, si nos equivocamos en el nombre dará un error por variable no definida.

Hay algunos lenguajes que se pueden configurar por la configuración del entorno de programación o usando una instrucción de pre-procesador en el propio programa para que se comporten de una manera o de la otra.

Variables globales y variables locales

En el BASIC estándar todas las variables son globales, eso significa que las podemos usar en cualquier parte del programa sin problema. Evidentemente en lenguajes donde definimos las variables antes de comenzar el programa en un bloque separado, como COBOL o en el ABAL de Bull, todas las variables son también globales.
 
En lenguajes que soportan funciones o procedimientos, existe la posibilidad de definir variables que solo se verán dentro de ellas, desapareciendo su valor al salir de las mismas (aunque en algunos se conservan los valores, perdiendo entonces parte de su ventaja). Estas son variables locales.

La idea de las locales surgió en ALGOL, donde existen bloques del programa, estos bloques se marcan con una instrucción de inicio y otra de fin, por ejemplo en C se usan las llaves {} mientras en PASCAL se usa begin y end. Dentro de estos bloques también podemos definir variables locales a los mismos.

Una variable local pueden usar el mismo nombre que una global, siendo ambas independientes, en la función se usará la variable local, y fuera de ella se usará la global, pero esto puede dar lugar a errores dentro de nuestras funciones por pensar que estamos usando la global, por eso hay que ser cuidadosos en el nombre de las variables. Para evitar errores es buena costumbre usar una letra antes del nombre, sobre todo en las locales, por ejemplo añadir loc delante.
 
Un uso muy habitual de una variable local en C es para los bucles definiendo las variables índice dentro del propio bloque, o incluso en la definición del mismo, por ejemplo for(int i=1;i<10;i++) { ... } esto es un bucle en donde i tendrá los valores del 1 al 9, y después del mismo desaparece. Veamos un ejemplo un poco mas completo:

(1)    int i=84;
(2)    do {
(3)        int i=6;
(4)        i++;
(5)        printf("%d", i);
(6)    } while (i<10) 
(7)    printf("%d", i);
 
La línea 5 presentará los valores de la variable i que es local al bucle: 6,7,8,9 y luego en la línea 7 presentará la variable global con valor 84.
 
En nuestro querido SuperBASIC se permie definir variables locales en las funciones y procedimientos, usando el comando LOC seguido de las variables que queramos definir. Por ejemplo
 
10 a=1 : b$="A"
20 print "1",a,b$
30 prueba
40 print "3",a,b$
50 Define PROCedure prueba
60     loc a,b$
70     a=5 : b$="U"
80    print "2",a,b$
90 End DEFine 
 
Al ejecutarlo imprimirá:
1   1   A
2   5   U
3   1   A 

En la siguiente entrada abordamos los punteros, de uso muy habitual en C, pero fuente de problemas por lo que se prefiere ocultados en otros lenguajes como Java, que los usan implícitamente sin que los veamos.


lunes, 15 de julio de 2024

Historia de la electricidad (XXIV): La guerra de las corrientes

 Índice de entradas sobre la Historia de la Electricidad


Esta pugna entre las empresas General Electric Company (empresa creada por Edison a partir de la Edison General Electric Company, junto a varios inversionistas, principalmente J.P. Morgan) y la Westinghouse Electric (empresa creada por  George Westinghouse, donde trabajaba Nikola Tesla, hoy propiedad de la Paramount), por hacerse con el control de la distribución de la electricidad en hogares e industrias. Aunque esta guerra fue "perdida" finalmente por Edison, ambas empresas se hicieron con una parte de la distribución de la electricidad en los EE.UU., crecieron mucho, diversificaron su producción (electrodomésticos, telefonía, radio, etc.) y con sus altibajos siguen existiendo hoy día y son conocidas a nivel mundial (en España la Westinghouse por sus electrodomésticos, mientras la GE se conoce más en aparatos industriales).

Corriente continua y corriente alterna

Tras las época de las baterías, los generadores de corriente usados hasta finales del 19 eran dinamos, que generaban corriente continua, ideados por Faraday tras sus estudios sobre la inducción electromagnética. De manera simplificada, en una dinamo se hacer girar unos imanes sobre un bobinado, la variación del campo magnético induce una corriente en la bobina, al hacer que los polos del imán se muevan siempre en la misma zona de espiras, se genera una corriente continua en estas..

Haciendo que los polos del imán cambien en su paso por las espiras, la corriente cambia de sentido, por lo que se genera corriente alterna, pero era complicado hacer un sistema que rotara los imanes, por lo que se recurrió a usar electroimanes en su lugar, pero el rendimiento de estos generadores era muy pobre..

Inicialmente la corriente continua tenia muchas ventajas en el laboratorio, se podía generar de manera muy sencilla una corriente del voltaje deseado cambiando la velocidad de movimiento del motor, y se estudia mucho mejor una corriente continua que si esta varía con el tiempo. Por eso los primeros intentos de producir electricidad para usos prácticos usaban algo que estaba bien desarrollado y estudiado en ese momento. Edison electrificó sus laboratorios con continua, y los iluminó con sus bombillas, por lo que tras vender la idea a J.P. Morgan, gracias a la inversión pudo hacer que su empresa, la Edison General Electric Company (en la que comenzó a trabajar Tesla en Europa y luego en EE.UU., ver entrada anterior), que se dedicaba principalmente a temas de telefonía, pusiera una central eléctrica en NuevaYork y comenzara a electrificar las casas.

Whestinhouse estaba trabajando en el generador de alterna, pero no conseguía mejorar el rendimiento, hasta que decidió probar a generar los electroimanes con la propia electricidad que generaba la máquina, idea en principio desechada por su analogía con el movimiento continuo y sus grandes pérdidas, pero la idea funcionaba, la retroalimentación del sistema permitía generar una corriente alterna con mejor rendimiento que con las dinamos, aunque los generadores eran mucho mas complejos.

Whestinhouse y Edison presentaron ofertas para electrificar la Exposición Universal de Boston, ganando el primero por ser mucho mas económico, lo que le  dió fama y le permitió comenzar a electrificar la iluminación de las calles con bombillas de arco usando su corriente alterna, pero aun así Edison ganó la primera batalla.

Los electrodomésticos y sus enchufes

Edison seguía convencido de que era mejor seguir con sus dinamos, pero como la distribución de la electricidad usando corriente continua tiene limitaciones de distancia, principalmente por la pérdida en forma de calor en los cables, su idea era crea muchas pequeñas centrales generadoras, una por cada barrio, de manera similar a como se hacía en ese momento con las centralitas manuales de los teléfonos. De esta manera se podía reemplazar el sistema de iluminación del momento tanto el de las calles como el de las casas, que eran principalmente las lámparas de gas, muy peligrosas por la posibilidad real de incendios y explosiones, por las bombillas de Edison, mucho mas sencillas, mucho mas económicas y de mayor duración que las bombillas de arco, por lo que en 1882 instaló la primera central de corriente continua en Manhatan. Para poder suministrar sin problemas, usaba una corriente de 100 Voltios, ya que a voltajes mas bajos las pérdidas eran mucho mayores.

Pronto fue muy popular su uso, y creció rápidamente para iluminar todo NuevaYork con sus bombillas, único aparato eléctrico en las casas. Cuando se crearon nuevos Electrodomésticos, estos necesitaban un enchufe, y en casa solo había lámparas, por lo que los primeros usaban un casquillo de bombilla como conexión, uno de los primeros electrodomésticos fueron las maquinillas eléctricas para afeitarse, por lo que para hacerlo debías desenroscar la bombilla y enroscar el enchufe de la maquinilla (hay alguna película de la época en que se ve esto). 

Como se vio que era necesario algo mas sencillo, se crearon los primeros enchufes, el primero fue patentado en 1882 por Thomas Tayler Smith, que estaba conectado también a la rosca del portalámparas. Luego se hizo que el propio portalámparas tuviera el conector para el enchufe y la rosca de la bombilla, por lo que en algunas películas antiguas se ve como la afeitadora está enchufada con la bombilla (si no recuerdo mal esto lo hace Clark Gable en la comedia de  Frank Capra "Sucedió una noche" de 1934, y Cary Grant en la también muy divertida "Luna nueva" de Howard Hawks en 1940, y aunque me falle la memoria, ambas son muy recomendables de ver  igualmente).

Niagra

No me refiero a la película de 1953 de Henry Hathaway, una buena película de cine negro, de las pocas de Marilyn Monroe fuera de la comedia, en cine comenzó con un drama y un breve papel donde no lo hizo mal en Peligrosa juventud de 1947, luego podemos destacar fuera de la comedia La jungla de asfalto de 1950 o la mencionada Niagra de 1953, Rio sin Retorno de 1954, aunque para mi sus mejores actuaciones fueron en Bus Stop y, sobre todo, en su última película completa, Vidas rebeldes (aunque a ratos sobre actúa un poco).

Para mover los generadores se necesita energía, la forma mas sencilla es usar un salto de agua, ya existía en el rio Niagra un canal paralelo a las cataratas, que aprovechaba la diferencia de altura y el gran caudal de agua para unos molinos. Se creó la empresa Niagara Falls Hydraulic Power & Manufacturing Company, la primera en generar energía hidroeléctrica a gran escala. 

undefined
Generadores en Niagra Falls (fuente: Wikipedia)

La corriente alterna tiene la ventaja de que se puede convertir de uno a otro voltaje, usando transformadores, componentes pasivos consistentes en bobinados en paralelo, no usan partes móviles, y apenas necesitan refrigeración (principalmente se usan radiadores de aceite para los de alta tensión).

Westinghouse pudo producir corriente alterna de alto voltaje, que luego se puede llevar a las ciudades por líneas de alta tensión, al llegar a las ciudades convertirla en media tensión, y luego en baja tensión para usarla en los hogares. 

Con la gran cantidad de electricidad que generaba, Westinghouse pudo llevar la electricidad a varias ciudades, llegando hasta a Nueva York a 600km de distancia.


La guerra de las corrientes y la silla eléctrica

Tras establecer ambos las bases de su negocio, cada uno de ellos luchó por hacerse con el mercado, compitiendo Edison-Westinghouse (aunque siempre se dice Edison-Tesla, este solo se ocupaba de la parte práctica de la generación de electricidad).

Edison usaba la ventaja de su fama, mientras que para poner de manifiesto la superioridad de su sistema, Westinghouse usaba su mejor baza, el precio mas bajo al poder generar mucha cantidad de electricidad y llevarla donde lo necesitara, en lugar de depender de montar muchas pequeñas centrales  cercanas. También cobro ventaja en la industria por sus motores eléctricos de corriente alterna (desarrollo de Tesla) que eran mas potentes que los de continua de Edison.

Ambas empresas acabaron enfrentándose en una batalla de relaciones públicas, que los diarios del momento calificaron como "la guerra de las corrientes", por ver quien se hacía con el mercado finalmente. Ocurrieron algunos accidentes en las líneas de alta tensión, lo que Edison magnificó moviendo sus contactos en la prensa, que hablaron a favor de su sistema que enterraba los cables bajo tierra, no en líneas aéreas. Pero Nikola Tesla se expuso a una CA que atravesó su cuerpo sin causarle ningún daño (si estás aislado de tierra, tu cuerpo puede conducirla sin ser afectado), lo que no pudo replicar Edison. 

undefined
Enterrando cables eléctricos de la Edison en Nueva York (fuente: Wikipedia)


Pero este contraatacó con la Silla Eléctrica. Esta fue ideada un empleado suyo llamado Harold P. Brown, que estaba buscando un sistema que reemplazara a la horca que se usaba en ese momento. Cuando una corriente alterna atraviesa un cuerpo, si es suficientemente grande producirá una interferencia en el sistema nervioso pudiendo llegar a la parada cardíaca, en teoría de forma muy rápida y de manera indolora. Para esto era mejor la alterna, pues es mas sencillo general altos voltajes con ella. 
 
Tras pruebas con varios animales, la gran demostración la hizo ante la prensa, donde entre otros animales la aplicó a un elefante de un circo. La prensa lo denominó "electrocución", y se adoptó desde 1889 como reemplazo de la orca en la mitad de los estados de EE.UU., principalmente en la zona este del país. Su primer uso fue en Nueva York en 1890, tuvo un auge, pero tras críticas por su uso se reemplazó gradualmente por la inyección letal, y en 2008 que fue abolido su empleo definitivamente.
 
Aunque Edison movió a la prensa todo lo que pudo a su favor, finalmente tuvo que rendirse a los echos, la alterna era mucho mejor sistema para distribuir la electricidad en hogares e industrias, no solo perdió la guerra, sino que quedó como el creador de la silla eléctrica, aunque no fuera cierto. En 1892 se desvinculó de la electricidad, se fusionó su empresa con la segunda mas grande del momento, la Thomson-Houston, creando la General Electrics.

martes, 9 de julio de 2024

Historia de la electricidad (XXIII): Tesla, un genio con altibajos

Índice de entradas sobre la Historia de la Electricidad



Tesla es una figura muy endiosada, su idea de la energía libre es comentada por todos, aunque la mayoría no sabe realmente lo que era, y la gran mayoría piensa que hubo un complot en su contra para evitar que la llevara a cabo, sin conocer nada del asunto. Intentaré dar una breve explicación de sus logros y de sus fracasos, sin estar de un lado o del otro, fue un genio, pero no todas sus ideas eran realizables.

Nikola Tesla (1856-1943)

Nacido en lo que hoy es Croacia, entonces una parte del imperio Autro-Húngaro, donde estudió ingeniería y física pero sin obtener un título. Tras varios trabajos, a los 24 se trasladó a París, donde comenzó a trabajar en la Continental Edison, la filial de Edison en Europa, que era el líder del momento implantando la electricidad y el teléfono en toda Europa.

En 1884 el director de la empresa es trasladado a EE.UU., invitando a Tesla a acompañarlo ya que era un gran ingeniero. Allí trabajó un tiempo en la Edison Machine Works, en Nueva York , antes de emprender el camino por su cuenta, ya que según parece la empresa le prometió un incentivo económico y no lo cumplió. Apoyado por sus buenas ideas, sus habilidades de orador y la gran presentación que hacía de sus experimentos, consigue financiación y crea su propio laboratorio, con su principal financiador, George Westinghouse, que había obtenido su fortuna como ingeniero ideando temas como el freno neumático para los ferrocarriles. En esta empresa y gracias a varias patentes propias ganó un pequeña fortuna

En la Exposición Mundial de Chicago de 1893, Tesla y Westinghouse iluminaron la exposición con corriente alterna, con lámparas fluorescentes y bombillas de Tesla de un solo nodo.

Obtuvo la doble nacionalidad, y como no le fue difícil conseguir financiación, trabajó duramente en sus ideas, algunas las llevó a algo práctico y otras no pudo conseguir hacerlas funcionar, especialmente por sus fracasos que motivaron la falta de inversores a partir de 1910 hasta su muerte.

Se dice que dormía 2 horas diarias, pero que echaba pequeñas siestas a lo largo del día. Esto es propio del insomnio, que a largo plazo provoca cierto desequilibrio mental, pero lo que acabó con su salud mental fueron sus fracasos, y sus problemas mentales se fueron agravando con la edad. En sus últimos años vivió en varios hoteles (cosa no tan rara en el momento, a Capone lo detuvieron en su suite del Hotel Lexington en 1928), que tenía que dejar al no pagar las facturas. 

El episodio de la paloma es muy tergiversado, era aficionado a largas caminatas (se dice que andaba 13 km diarios) y a alimentar a las palomas en los parques. Una de estas acudía siempre cuando el estaba, y le cogió gran cariño, pero la forma de describir este cariño cuando tenía ya problemas mentales no fue la mas adecuada, dijo que se querían como una pareja de enamorados.

Ideas que fueron un éxito, y otras que no

Entre las muchas cosas que creó o mejoró a lo largo de su vida, cabe destacar las dos pugnas que tuvo, la de Edison por la electricidad y la de Marconi por la telegrafía inalámbrica, en ambas ramas aportó aparatos que fueron importantes.

Comenzó en la Edison, mejorando las dinamos para generar continua. Luego para distribuir la electricidad usando la corriente alterna ideó un sistema polifásico, mejoró los generadores diseñados por Westinghouse, mejoró las bombillas y los fluorescentes. Para llevar su corriente a las empresas ideó un motor eléctrico de corriente alterna (no inventó el motor eléctrico, ya estaba echo, ni inventó uno de alterna que ya había intentos, cogió lo que había, lo hizo eficaz y lo patentó, lo que ya es suficiente para pasar a la historia, y tampoco es cierto que los coches Tesla usen su motor, en 100 años han mejorado muchísimo los motores).

Diseñó la Bobina de Tesla, con la que se consiguen altos voltajes a alta frecuencia (las famosas fotos de Tesla rodeado de rayos son producidos por sus bobinas), y mejoró los tubos fluorescentes.

En el tema de la radio, aunque diseñó un barco radio controlado, que intentó vender a las marinas como una mina por control remoto, no consiguió vender la idea. Trabajó en transmisión inalámbrica de energía (que solo funcionaba a cortas distancias como hoy día con los cargadores inalámbricos), y las emisiones de radio.

Pero también hubo cosas que no funcionaron o que solo fueron ideas que no desarrolló, Trabajó en los rayos X, pero no aportó mejoras importantes. Tuvo la idea de un aparato parecido a un sonar para detectar submarinos, pero su idea era usar algo similar al radar, lo que hoy se sabe que no podía funcionar bajo el agua, Ideó el Teslascopio, aparato para hablar con seres del espacio exterior.

Llegó a patentar un avión biplano con despegue vertical, poner el avión en vertical para que despegue y luego que pase a vuelo horizontal. Esta idea se ha intentado varias veces, como con el Lockheed XFV, pero nunca ha conducido a algo realmente operativo. El primer avión VTOL fue el Harrier de 1958, y no usaba esta idea.

Su gran error y las falacias actuales

El mayor fracaso de Tesla fue la energía libre, pensaba en un sistema de transmisión de la energía que está en la atmósfera y en la propia tierra, por tanto energía libre, usando torres de radio. La idea no era buena, el desarrollo es imposible como han demostrado los físicos, ya que su idea era recoger la energía de la atmósfera y remitirla usando la tierra, lo que no sabía era que las ondas transversales que pretendía enviar son absorbidas rápidamente, por lo que la idea es imposible. Sus modelos eran muy limitados en tamaño y alcance, pero insistió en la idea sin realizar mejores experimentos, lo que lo llevó a fracasar tras invertir mucho dinero (J.P.Morgan invirtió el equivalente a 4 millones de dólares actuales en el proyecto) y construir la Torre Wardenclyffe a 160Km de Nueva York, con 57 metros de altura, construida entre 1901 y 1917 pero nunca completada, que fue desmantelada poco después.

Sobre esto hay una mentira que circula mucho, por una falacia que se da mucho actualmente, se piensa en términos actuales y con ideas de hoy sobre cosas históricas, lo que no es posible nunca. Al llamarla "energía libre", muchos piensan en lo que hoy se entiende por libre, que sea algo gratuito, por lo que sin pruebas muchos piensan que su idea era generar energía gratis para todos. Esto no es cierto, la idea tenía dos partes, el generador y el receptor, y los receptores sí eran de pago, con contadores para poder cobrar a los abonados. Solo hay que pensar que ningún financiero hubiese puesto dinero en una idea sin obtener beneficios por su inversión.

Basando en esto, y con la idea de que electrificar el celebro era beneficioso (nuevamente sin ningún estudio que lo corrobore), pensaba que rodeando las aulas de los colegios con cables que indujeran electricidad en los alumnos, estos mejorarían su rendimiento notablemente.

También se publicó en los periódicos de la época que les darían el Nobel conjunto a Edison y a él, lo que solo fue un bulo, la academia nunca pensó en un nobel solo por sus logros de ingeniería, necesitan estar respaldados por logros científicos, como el caso de Marconi, que lo recibió no solo por el desarrollo práctico, sino que también contribuyo al marco teórico y al estudio de las ondas radioeléctricas, como la ley de Marconi para el cálculo del alcance de las señales de radio en función de la altura de ubicación de la antena.

Es sabido que Tesla no creía en la teoría atómica, pensaba que los electrones eran solo un estado de la materia y no una partícula. También afirmó tener un desarrollo revolucionario sobre la gravedad, pero no se han encontrado nunca información sobre el tema entre sus papeles.

lunes, 8 de julio de 2024

Historia de la electricidad (XXII): Edison, mucho mas que la bombilla

Índice de entradas sobre la Historia de la Electricidad



En esta estrada hablaré de Edison, en la siguiente de Tesla, y luego dedicaré una tercer a su muy famosa guerra de las corrientes.

Todos saben que Edison ideó la bombilla en 1879, pero el historial de aparatos desarrollados por el es muy numeroso, y su mejor creación fue Melo Park, de donde surgieron todos sus inventos.

Thomas Alva Edison (EE.UU. 1847-1931)

Cuando empezó a ir a la escuela, el profesor le calificó como "estéril e improductivo", por lo que su madre tomó en sus manos la educación de su hijo, fomentando la afición a la lectura que le acompaño toda su vida.

A los 12 años comenzó a trabajar, vendía comida y periódicos en el tren, aprovechando las largas paradas para ir a la biblioteca, siguiendo un orden muy especial, cogía el primer libro de un estante, luego el siguiente, y así avanzaba. En el tren se aburría, por lo que se montó un laboratorio en uno de los vagones, donde puso una pequeña imprenta y empezó a publicar su propio diario.

Como agradecimiento por salvar a su hijo, el telegrafista de la estación le enseñó el código y la telegrafía, por lo que a los 15 años pasó a ser telegrafista. Mientras trabajaba en varias oficinas de telégrafos, siguió experimentando, lo que condujo a su primer invento, el Repetidor Automático, que retransmite las señales entre estaciones sin necesidad de personal, pero que nunca patentó.

Su primera patente fue un aparato para contar los votos en las elecciones en el congreso, con dos botones: a favor y en contra (hoy día se usa ese mismo sistema). El congreso lo rechazó alegando que su idea fomentaba los fraudes.

Entre 1869 y 1877 trabajó haciendo reparaciones en empresas telegráficas, primero com empleado y luego por su cuenta, sin dejar de investigar nunca, por lo que en 1877 presentó su primer invento importante, el fonógrafo, el primer aparato común para grabar y reproducir sonido, usando un cilindro de cera para grabar los sonidos con una aguja. Este invento lo lanzó a la fama mundial.

La fábrica de inventos

Entre 1876, Edison se mudó a Menlo Park, en Nueva Jersey. Allí creó un lugar donde reunió a científicos, ingenieros y mecánicos, y estableció una "fábrica de inventos", que mantuvo hasta 1887, con casi 400 patentes en esos 11 años.

Su idea era utilizar los conocimientos combinados de todos para desarrollar aparatos innovadores, no siempre ideas suyas, pero si hacerlos realmente útiles, como veremos en el tema de la bombilla.

El micrófono de carbón

En 1876 patentó un micrófono de carbón, que revolucionó la telefonía al ser mucho mas sensible que lo anterior, que era el usado en los teléfonos de sobremesa, desaparecidos hace muy poco, y que todavía se sigue usando, aunque los sistemas mas modernos permiten hacer micrófonos mucho mas pequeños. 

Entre dos láminas metálicas separadas por gránulos de carbón, se hace pasar una corriente, la vibración del sonido se transmite a través de los gránulos, modulando la señal eléctrica.

La Bombilla y la electrónica

Aunque se piensa que Edison inventó la bombilla, no es correcto, ya existían varios diseños anteriores, pero que no duraban mas de unos minutos antes de fundirse. Edison lo que hizo es que fuera algo útil con una duración adecuada, y con ella iluminar el mundo.

Inicialmente lo intentaron  Henry Woodward, Mathew Evans, James Bowman Lindsay, William Sawyer y Warren de la Rue, pero sin alcanzar su objetivo. En 1855 Heinrich Göbel (Alemania) había registrado su idea, y en 1874 Aleksandr Lodygin (Rusia) hizo lo mismo.

John Wellington Starr (EEUU 1822-1846)  en 1845 patenta una lámpara incandescente, pero no consiguió que durara mas de minutos. 

Joseph Wilson Swan (Inglaterra, 1828-1914) a partir de los trabajos de Wellington lo intenta pero nuevamente sin obtener mas que minutos, principalmente por que su sistema de vacío no era muy eficiente, pero si consiguió mantener varias bombillas en un solo circuito, cosa que antes era imposible. Patenta su invento en 1879. Posteriormente fundaría la Swan Electric Light Company y comenzaría a electrificar hogares y alumbrarlos con su bombilla.

Todos lo intentaron con filamentos metálicos o de carbono, pero sin obtener algo realmente operativo. En este punto Edison empieza sus investigaciones, metódicamente probó con mas de 2.000 filamentos, hasta encontrar que un hilo de bambú carbonizado estuvo 48 horas encendido. Patentó su bombilla de filamento de carbón, y con ella iluminó su laboratorio.

Su bombilla fue muy popular, pero tenía un problema, el carbón se desprendía del filamento, oscureciendo el cristal. Estudiando la forma de evitarlo, introdujo un segundo filamento con la esperanza que atrajera los restos, descubrió que no era así, introdujo una chapa metálica rodeando el filamento principal, tampoco conseguía lo deseado, pero como era un gran observador, notó que al variar la corriente en esa chapa alteraba la corriente del segundo, lo que se conoce como efecto termoiónico o efecto Edison, que ya se conocía anteriormente al calentar objetos cargados, pero cuando Edison lo describió, permitió que se investigara el fenómeno de manera mucho mas sencilla, lo que posteriormente  fue el origen de las lámparas termo iónicas, lámparas de vacío, o simplemente lámparas, que abrieron el camino a la moderna electrónica. 

El cine

Edison fue uno de los pioneros en el cine, tras ver el invento de los Lumiere, se interesó en el sistema, desarrolló sus propias patentes, ideó una máquina que denominó kinetoscopio en 1889, una caja cerrada con un pequeño visor, dentro tenía una película de cine en un bucle y una bombilla. Comenzó a poner máquinas en las ferias, que por una moneda permitían ver unos minutos de una película, siendo muy conocido una de un combate de boxeo importante de la época, para verlo completo había que usar varios kinetoscopios. Intentó una variante sonora con el kinetófono, pero no acabó de funcionar bien al no sincronizar voz e imagen adecuadamente, aun así se conservan tres pequeñas muestras de lo que podemos denominar el primer cine sonoro.

Este aparato está basado en uno de 1888 denominado zoopraxiscopio de  Eadweard Muybridge, proyectaba fotografías una tras otra usando un pequeño carrusel, lo que daba impresión de movimiento, según dicen quiso asociarse con Edison para añadir sonido con su fonógrafo, pero Edison vio el potencial y patentó su propio invento, mucho mas desarrollado.

Posteriormente, creó la Edison Studios en New York, primera productora americana, donde comenzó a grabar sus propias películas, destacaré la primera versión de Frankenstein en el cine en 1910, con 14 minutos de duración.

Pero también distribuía copias ilegales de otras películas, principalmente las europeas y sobre todo las de Georges Méliès. Esto creaba denuncias continuas, lo que le llevó a intentar distanciarse de New York, por lo que la trasladó a California, entonces muy alejando, por lo que es el iniciador del moderno Hollywood.

Las polémicas

Hay una gran polémica sobre que no hizo nada, solo se apropió de ideas y patentes de otros, lo que solo es parcialmente cierto, sin querer justificarlo, si vemos la historia de la bombilla o la del cine, en algunos casos usa sus ideas, en otros se basó en ideas de otros, en otros se las apropió, pero a diferencia de los demás, Edison convertía una idea en un producto útil y práctico, su gran mérito la mayoría de veces no fue crear algo, sino hacerlo práctico y que se pudiera usar, y ponerlo al alcance de todos.