Programación Metamórfica

Ensayos hipotéticos sobre la tecnología ybymarense —

    Un código metamórfico es aquel que tiene la peculiar característica de cambiarse a sí mismo independientemente del programador. Para ello se introducirá un nuevo tipo de variable que se puede denominar variable logicial. A grandes rasgos, la idea es que las líneas de código del programa se almacenen en sus propias variables. De esta for­ma, cambiar las variables del código altera su comportamiento.

    1. Conceptos iniciales de programación metamórfica

    Una variable lógica puede contener un comando, función o valor. Para identificar una variable lógicial, puede ir seguida de un símbolo. En este trabajo se utilizará para ello el carácter «@». Veamos como ejemplo la variable lógica denominada VAR.

VAR@ = #PRINT → VAR contiene el comando “PRINT”
VAR@ = #SIN → VAR contiene la función “SIN”
VAR@ = “H” → VAR contiene el carácter “H”
VAR@ = 10 → VAR contiene el valor numérico 10

    Una matriz o vector de estas variables contiene una secuencia de comandos y va­lores. Tenga en cuenta que el indicador «#» indica una función o comando. No es nece­sario que las constantes numéricas o alfanuméricas vayan precedidas de símbolos.

    Veamos un ejemplo de un vector o matriz de variables lógicas unidimensional:

VAR@[0] = #PRINT
VAR@[1] = #SIN
VAR@[2] = 10

    Esta secuencia forma una línea de comando:

VAR@[0]VAR@[0]VAR@[0]
PRINTSIN10

    La línea de comando resultante imprimirá el valor del seno de 10 en la salida es­tándar. Podemos usar un comando para llamar a una variable lógica, como por ejemplo:

CALL VAR@

    Este comando hará que se ejecute el contenido de VAR@. En este caso, el valor seno de 10 se imprimirá en el dispositivo estándar (pantalla, por ejemplo). Esta caracte­rística, en sí misma, no agregaría nada a lo que pueden hacer los lenguajes de progra­mación actuales, ya que es solo un comando de tipo «eval()» desglosado en sus factores en lugar de una cadena completa. Los programas siguen siendo rígidos.

    El objetivo de la programación metamórfica es permitir que los comandos conte­nidos en variables lógicas sean creados, eliminados y modificados mediante datos e in­formación que se insertan en el sistema. Esto es relativamente sencillo. La cuestión es cómo hacer que la secuencia de comandos resultante tenga sentido y evitar que el siste­ma colapse o entre en un bucle infinito, lo que puede ocurrir, por ejemplo, en el caso de comandos anidados o recurrentes. Un ejemplo:

VAR@[0] = #PRINT
VAR@[1] = #SIN
VAR@[2] = 10
VAR@[3] = #CALL
VAR@[4] = VAR@

    Esta secuencia de comandos dentro de VAR@ hará que las instrucciones conteni­das en VAR@ se ejecuten indefinidamente si se llama a la variable. Para evitar esto, una de las reglas puede ser evitar que una variable lógica se llame a sí misma. Esta es la so­lución sencilla y cruda.

    Centrémonos en el objetivo de la programación metamórfica, que es la inteligen­cia artificial, lo que significa que los comandos habituales en los lenguajes de progra­mación convencionales pueden no ser adecuados. Aun así, aquí se mantendrá el esque­ma «entrada → procesamiento → salida», donde el resultado (salida) será utilizado por otro comando en una secuencia de longitud indefinida y variable.

    Digamos que tene­mos un comando que suma dos valores de 16 bits y entrega el resultado limitado a 16 bits también. Veamos la secuencia a continuación (tenga en cuenta que el indicador «&» se usó para indicar la entrada o salida de datos).

Soma@[0] = #ADC
Soma@[1] = &Entrada1
Soma@[2] = &Entrada2
Soma@[3] = #OUT
Soma@[4] = &Saída1

    Esta secuencia de comandos hace que el sistema sume los valores de «Entrada1» con los de «Entrada2» (función #ADC) y los dirija (#OUT) a «Salida1». La estructura es básicamente la siguiente:

    1. Operación a realizar
    2. Definición de puertos de entrada
    3. Comando de salida
    4. Definición de puertos de salida

    Si se mantiene esta estructura, lo más probable es que el sistema no colapse debi­do a algún error de comando. Un código de programación con esta filoSofia podrá estar formado por un número variable de bloques con esta estructura. Sin embargo, las entra­das y salidas deben definirse antes de la secuencia de bloques. Vea la sugerencia a con­tinuación:

DEF &Entrada1 as input = Keyboard;
DEF &Entrada2 as input = Mouse;
DEF &Salida1 as output = Video;
DEF Suma@ as logicial_variable;
    Suma@[0] = #ADC
    Suma@[1] = &Entrada1
    Suma@[2] = &Entrada2
    Suma@[3] = #OUT
    Suma@[4] = &Salida1
    CALL Suma@;
END ROUTINE;

    Tal como está, la rutina toma un valor del teclado, otro del mouse, suma los dos y devuelve el resultado en el monitor de video. La estructura permanece rígida. Tenga­mos en cuenta también que las entradas y salidas dependen del hardware y solo varían a medida que cambia este. Debido a esto, por ahora consideraremos las definiciones de entrada y salida como valores invariantes.

    Continuando con el razonamiento, asociaremos un valor numérico a cada coman­do. Así, tendremos:

    1 = #ADC
    2 = #SUB
    3 = #OUT
    4 = &Entrada1
    5 = &Entrada2
    6 = &Salida1
    7 = CALL

    De esta forma, los valores numéricos contenidos en la secuencia quedarían de la siguiente manera:

Suma@[0] = #ADC      ;valor 1
Suma@[1] = &Entrada1 ;valor 4
Suma@[2] = &Entrada2 ;valor 5
Suma@[3] = #OUT      ;valor 3
Suma@[4] = &Salida1   ;valor 6

    Supongamos que las entradas son numéricas. Entonces, por ejemplo, si tenemos 2 en &Entrada1 y 3 en &Entrada2, el valor 5 se enviará a &Salida1. Agreguemos una línea más al código:

Suma@[0] = #ADC      ;1
Suma@[1] = &Entrada1 ;4
Suma@[2] = &Entrada2 ;5
Suma@[3] = #OUT      ;3
Suma@[4] = &Salida1   ;6
Suma@[0] = (&Entrada1)

    Notemos que el valor contenido en la posición Suma@[0] variará según el valor de &Entrada1. En esta posición se encuentra el comando #ADC, cuyo número asocia­do es el 1. Si ingresamos los mismos valores, tendremos:

Suma@[0] = #ADC        ;Comando de suma
Suma@[1] = &Entrada1   ;Valor 1ª entrada (2)
Suma@[2] = &Entrada2   ;Valor 2ª entrada (3)
Suma@[3] = #OUT        ;Comando de salida
Suma@[4] = &Salida1    ;Valor de salida (5)
Suma@[0] = (&Entrada1) ;Pone el valor de "&Entrada1" en Suma@[0] (comando SUB)
CALL Suma@             ;Ejecuta

    * Código después de la primera ejecución:

Suma@[0] = #SUB       ;Comando de suma
Suma@[1] = &Entrada1  ;Valor 1ª entrada
Suma@[2] = &Entrada2  ;Valor 2ª entrada
Suma@[3] = #OUT       ;Comando de salida
Suma@[4] = &Salida1    ;Valor de salida
Suma@[0] = &Entrada1  ;Pone el valor de "&Entrada1" en Suma@[0]

    Observemos que un valor de entrada cambió el código del programa. Este es el concepto principal de la programación metamórfica.

    2. Estructura de un lenguage metamórfico

    Un código metamórfico está formado básicamente por vectores o cadenas de ta­maño variable donde cada posición puede contener un valor numérico (cuantitativo), un valor o carácter alfanumérico (cualitativo) o un valor de ejecución (comando o fun­ción). El número de vectores también es variable y puede cambiar en tiempo de ejecu­ción. Podemos, por ejemplo, crear el vector del código ilustrado arriba (ya con los defi­nidores de variables):

    DEF&Entrada1 as input = Keyboard;
    DEF&Entrada2 as input = Mouse;
    DEF&Salida1 as output = Video;
VECTOR[1] = Suma@ {
    0 = #ADC
    1 = &Entrada1
    2 = &Entrada2
    3 = #OUT
    4 = &Salida1
    0 = &Entrada1
    }

    Tenga en cuenta que los definidores están fuera del vector, lo que significa que no se pueden cambiar. Puede haber comandos fuera de los vectores; en este caso su estruc­tura será rígida como los códigos estructurados conocidos. En cualquier caso, podemos ver que los códigos metamórficos serán muy diferentes a otras clases de código, inclui­dos sus tipos de comandos y variables.

    Una pregunta básica a resolver es cómo funcionará el sistema si un valor ingresa­do no corresponde a ningún comando o definidor de variable. Digamos que es el valor 50. En el ejemplo, no hay ningún comando asociado con este número. Veamos qué pa­sa.

    Código original:

    DEF &Entrada1 as input = Keyboard;
    DEF &Entrada2 as input = Mouse;
    DEF &Salida1 as output = Video;
VECTOR[1] = Suma@ {
    0 = #ADC
    1 = &Entrada1
    2 = &Entrada2
    3 = #OUT
    4 = &Salida1
    0 = &Entrada1
    }
&Entrada1 = 50
&Entrada2 = 40

    Después de la primera ejecución (sin definidores de variables):

VECTOR[1] = Suma@ {
    0 = %50
    1 = %50
    2 = %40
    3 = #OUT
    4 = %90
    0 = %50
    }
(&Entrada1) = 50
(&Entrada2) = 40

    En el ejemplo, se utilizó el identificador «%» para indicar un valor numérico. Ten­ga en cuenta que no hay más comandos de suma, quedando solo el comando de salida. Esto sucede siempre que el código sea como se ilustra. Una mejor manera de tratar las variables contenidas en los vectores es mantenerlas del mismo tipo después de su crea­ción. Así, los índices 0 y 3 siempre serán comandos y los índices 1, 2 y 4 serán valores de entrada o salida. Se debe asociar una lista de comandos a cada valor numérico y se utilizará una regla de sustitución cuando no exista ningún comando correspondiente al número ingresado.

    Entonces, después de ejecutar estas reglas, el código se verá así:

VECTOR[1] = Suma@ {
    0 = #NUEVO COMANDO
    1 = &Entrada1
    2 = &Entrada2
    3 = #OUT
    4 = &Salida1
    0 = &Entrada1
    }
(&Entrada1) = 50
(&Entrada2) = 40
(&Salida1) = 90

    Sin embargo, las variables numéricas insertadas no tienen una posición específica dentro del vector, lo cual no es deseable. Así que creemos una posición para cada uno:

VECTOR[1] = Suma@ {
    0 = #NUEVO COMANDO
    1 = &Entrada1
    2 = &Entrada2
    3 = #OUT
    4 = &Salida1
    5 = VAL (&Entrada1)
    6 = VAL (&Entrada2)
    7 = VAL (&Salida1)
    0 = VAL (&Entrada1)
    }

    Así, los valores de &Entrada1, &Entrada2 Y &Salida1 se almacenarán en las posiciones 5, 6 y 7, respectivamente, y las posiciones 1, 2 y 4 continúan con su fun­ción de entrada y salida. Tenga en cuenta que en la última línea, que reasigna la posi­ción 0, el valor (&Entrada1) está entre paréntesis para indicar que esta no es una posición de entrada, sino una simple reasignación de valor a la posición, lo que resultará en el cambio del comando. Asimismo, en las posiciones 5, 6 y 7 los descriptores están en­tre paréntesis por el mismo motivo.

    Hasta ahora, hemos definido cuatro tipos de datos que se pueden insertar en posi­ciones vectoriales:

    Identificador # → comando o función.
    Identificador & → entrada o salida de datos.
    Identificador % → valor numérico o cuantitativo.
    Identificador $ → valor descriptivo o cualitativo.

    Los tipos no se pueden cambiar una vez definidos; de lo contrario, el código que­dará inútil. Cambiar el contenido de los índices vectoriales también cambia el compor­tamiento del código; sin embargo, el mismo valor significará cosas diferentes según el identificador. Por ejemplo, el valor 5 en un índice podría significar:

    #5 → comando AND (operador AND lógico)
    &5 → entrada de datos (digamos, &Entrada1)
    %5 → valor numérico entero 5
    $5 → carácter “A”

    2.1. Comandos únicos de un lenguaje metamórfico

    Entre los comandos posibles hay dos que son especiales y, se podría decir, exclusi­vos de los códigos metamórficos. Estos son los comandos #INC y #EXC, que signifi­can «Incluir» y «Excluir». Miremos el siguiente ejemplo para ver qué hacen estos co­mandos.

VECTOR[1] = Suma@ {
    0 = &Entrada1
    1 = #OUT
    2 = &Salida1
    3 = #INC
    4 = 0
    5 = "#ADC"

    }

    Después de la primera ejecución:

VECTOR[1] = Suma@ {
    0 = #ADC
    1 = &Entrada1
    2 = #OUT
    3 = &Salida1
    4 = #INC
    5 = 0
    6 = "#ADC"
    }

    Y para el comando #EXC:

VECTOR[1] = Suma@ {
    0 = &Entrada1
    1 = #OUT
    2 = &Salida1
    3 = #EXC
    4 = 0
    }

    Después de la primera ejecución:

VECTOR[1] = Suma@ {
    0 = #OUT
    1 = &Salida1
    2 = #EXC
    3 = 0
    }

    2.2. Ejemplos de comandos

    Cambiar comandos

INC – Incluye una celda en el vector.
EXC – Elimina una celda del vector.
SUBS – Reemplaza una celda en el vector.

    Comandos de operación

ADC – Agrega las siguientes dos celdas.
SBC – Resta la siguiente celda de la siguiente.
MULT – Multiplica las dos celdas siguientes.
DIV – Divide la siguiente celda entre la siguiente.
REST – Devuelve el resto de dividir la siguiente celda por la siguiente.
AND – Realiza una operación lógica AND entre las dos celdas siguientes.
OR – Realiza una operación lógica OR entre las dos celdas siguientes.
XOR – Realiza una operación lógica XOR entre las dos celdas siguientes.
NOT – Invierte la condición lógica de la siguiente celda.

    Comandos de Entrada/Salida:

DEF &xxx AS INPUT = DISP → Asigna <DISP> a xxx como entrada
DEF &yyy AS OUTPUT = DISP → Asigna <DISP> a xxx como salida

    Comandos de creación y ejecución

VECTOR [num] = zzz@ → Crea o vector número <num> con nombre <zzz>
CALL zzz@ → Ejecuta el vector <zzz>

    3. Estructura de um vector metamórfico

    Cada índice del vector debe tener un tamaño fijo, digamos 32 bits. Algunas de ellas deben reservarse para definir qué tipo de variable lógica contiene. Reservar 4 bits definirá hasta 16 tipos, dejando 28 bits para un valor numérico entero de 0 a 268.436.455 (o de –134.217.728 a 134.217.727, o cualquier otro valor representable).

    En cada posición del vector se puede almacenar un comando, una función, un va­lor numérico, un carácter alfanumérico, un descriptor de variable, etc. Para cada uno de ellos existe un ID específico, que no se puede cambiar una vez creado. Un ejemplo de identificación:

00 – Marca el inicio del vector.
01 – Comando
02 – Función
03 – Marcador de salida de datos
04 – Marcador de entrada de datos
05 – Valor entero sin signo (0 a 268.436.455)
06 – Valor entero con signo (–134.217.728 a +134.217.727)
07 – Exponente
08 – Primera mitad de la mantisa
09 – Segunda mitad de la mantisa
10 – Exponente imaginario
11 – Primera mitad de la mantisa imaginaria
12 – Segunda mitad de la mantisa imaginaria
13 – Carácter alfanumérico
14 – Carácter ideográfico
15 – Marca el final del vector.

    Según lo acordado, los cuatro bits de identificación no se pueden modificar una vez creados; solo los 28 bits restantes son variables.

    En algunos casos, algunas celdas se pueden agrupar, como los ID 7, 8 y 9, para componer un número de punto flotante, o en el caso del comando ADC se puede agru­par con los dos valores siguientes. Dichos grupos pueden formar un bloque (cuya es­tructura no puede modificarse) o una secuencia de estructura flexible.

Rolar para cima