El lenguaje de los DatosInform como lenguaje de programaciónInform como lenguaje de programaciónEl lenguaje de las Rutinas

El lenguaje de las Rutinas

Principios

El propósito de este capítulo es el de introducir a los principiantes a Inform como si se tratara de un lenguaje de programación de propósito general, en lugar de una herramienta específica para diseñar aventuras de texto. Los ejemplos dados serán programas cortos que realizan cálculos simples, en lugar de juegos. Para comenzar, el lenguaje Inform es:

  1. Compilado
    Esto es, el compilador de Inform traduce el texto escrito por el autor (llamado "código fuente") en un programa (llamado "código objeto", dado que es el objetivo del ejercicio). Esta traducción sólo se realiza una vez, pero el programa resultante puede ejecutarse muchas veces.
  2. Estructurado
    Esto es, el programa se divide en un número de "rutinas" (también llamadas "funciones" o "procedimientos"), cada una de las cuales es una lista de órdenes que deben ser ejecutadas (aunque estas órdenes se llaman tradicionalmente "instrucciones"). Cuando se ejecuta el programa, sólo una cosa ocurre a la vez: en cualquier momento dado, una sola rutina está siendo ejecutada.
  3. Portable
    Esto es, una vez que Inform ha compilado el código fuente (sin haber encontrado errores), el programa resultante puede ejecutarse en casi cualquier modelo de ordenador. Mostrará exactamente el mismo comportamiento en cada uno de ellos. No depende del "entorno": funcionará igual independientemente de la plataforma en la que se ejecute.

El ordenador ejecuta un programa en Inform (que no tiene por qué ser un juego) con la ayuda de un "intérprete". Existen al menos 40 intérpretes diferentes disponibles para este formato (llamado "máquina-Z" o "formato Infocom") y puede que haya varios para tu modelo de ordenador: es una buena idea tratar de conseguir el más moderno y preciso posible. Fíjate en si soportan el standard de la máquina-Z, y, si es así, hasta qué número de revisión.

Ejemplo 1: Hola, mundo

Tradicionalmente, todos los cursos de lenguajes de programación comienzan dando un programa que no hace nada más que imprimir "Hola, mundo". Aquí hay un programa de ese estilo en Inform:

! Programa de ejemplo "Hola, mundo"

[ Main;
  print "Hola, mundo^";
];

El texto tras el signo de exclamación es un "comentario", esto es, un texto escrito al margen por el autor para recordarse a sí mismo (o a las otras personas que puedan encontrarse leyendo ese código) qué es lo que ocurre en el programa. Tal texto no significa nada para Inform, que se limita a ignorar todo lo que haya en la misma línea y a la derecha del signo de exclamación.

Una vez que se han sacado los comentarios, Inform considera el código fuente como una lista de cosas a hacer, divididas por punto y coma ';'. Trata los retornos de carro, las tabulaciones y los espacios como "espacio en blanco": esto es, un agujero entre dos cosas cuyo tamaño no es importante. Así, podría producirse exactamente el mismo programa con este código fuente:

        [
           Main   ;
   print
           "Hola, mundo^"          ;
         ]
     ;

o, en el otro extremo, con este otro:

[ Main;print"Hola, mundo^";];

La legibilidad de un programa es una cuestión de buenos hábitos de formateo.

(!) La excepción a la regla por la que se ignoran los espacios en blanco es el texto entrecomillado, donde

"Hola,     mundo^"

y

"Hola, mundo^"

son dos fragmentos de texto genuinamente diferentes y que serán tratados como tales. Inform trata el texto entrecomillado con mucho más cuidado que el otro material del programa: por ejemplo, un signo de exclamación encontrado dentro de unas comillas no hará que el resto de la línea se deseche como un comentario.

Cada programa debe contener una rutina llamada Main, y en este ejemplo es la única rutina. Cuando un programa está siendo ejecutado, la primera instrucción obedecida es la primera de Main, y luego se continúa línea por línea a partir de ahí. Este proceso es llamado "ejecución". Cuando la rutina Main termina, el programa para.

La rutina tiene sólo una instrucción:

print "Hola, mundo^"

Imprimir es el proceso de escribir texto en la pantalla del ordenador. La instrucción print imprime las dos palabras "Hola, mundo" y luego se salta el resto de la línea (o "imprime una nueva línea"): el caracter ^, en un texto entrecomillado, significa "nueva línea". Por ejemplo, la instrucción

print "Azul^Rojo^Verde^"

imprime:

 Azul
 Rojo
 Verde

print es una de las 28 instrucciones del lenguaje Inform. La lista completa es la siguiente:

    box        break      continue   do       font      for         give
    if         inversion  jump       move     new_line  objectloop  print
    print_ret  quit       read       remove   restore   return      rfalse
    rtrue      save       spaces     string   style     switch      while

(Sólo unas 20 de estas se usan normalmente). La sección actual trata de todas las instrucciones no relacionadas con objetos. Estas últimas se tratarán en la sección * .

Ejemplo 2: La Mancha

El siguiente código fuente tiene tres rutinas, Main, Quijote y Sancho :

[ Main;
  print "Saludos desde un lugar de la Mancha...^";
  Quijote();
];
[ Quijote;
  print "... de cuyo nombre...^";
];
[ Sancho;
  print "...no quiero acordarme.^";
];

El programa resultante imprime

 Saludos desde un lugar de la Mancha...
 ... de cuyo nombre...

pero el texto "...no quiero acordarme." no se imprime. La ejecución comienza en Main , y "Saludos desde un lugar de la Mancha..." se imprime; después, la instrucción Quijote() hace que se ejecute la rutina Quijote . Así continúa hasta que termina con el marcador de final de rutina ']', con lo que la ejecución vuelve a la rutina Main justo tras el punto en el que se dejó; dado que no hay nada más que hacer en Main , el programa finaliza. Así, Quijote se ejecuta pero Sancho no.

De hecho, cuando se compila el programa de arriba, Inform se da cuenta de que Sancho no es necesaria e imprime una advertencia indicándolo. El texto exacto producido por Inform varía de una máquina a otra, pero es algo parecido a esto:

 DJGPP Inform 6.15 (22nd March 1998)
 line 8: Warning: Routine "Sancho" declared but not used
 Compiled with 0 errors and 1 warning

Los errores son fallos en el programa que hacen que Inform se niegue a compilarlo, pero esto es sólo una advertencia. Hace notar al programador que puede que se haya equivocado (porque presumiblemente ha olvidado poner una instrucción que llame a Sancho) pero no impide que la compilación tenga lugar. Obsérvese que la rutina Sancho comienza en la octava línea del programa anterior.

Normalmente se producen fallos en un programa escrito desde cero y uno ha de pasar por un proceso de ejecutar un primer borrador, recibir unos cuantos mensajes de error, corregir el borrador de acuerdo con los mensajes, e intentarlo de nuevo. Un mensaje de error típico habría aparecido si, en la línea 3, hubiésemos escrito Qujote() en lugar de Quijote() . Inform habría producido entonces:

 DJGPP Inform 6.15 (22nd March 1998))
 line 5: Warning: Routine "Quijote" declared but not used
 line 8: Warning: Routine "Sancho" declared but not used
 line 3: Error: No such constant as "Qujote"
 Compiled with 1 error and 2 warnings (no output)

El mensaje de error significa que en la línea 3 Inform se encontró con un nombre que no corresponde a ninguna entidad conocida (no es el nombre de ninguna rutina, en particular). Obsérvese que Inform nunca produce el archivo de juego final si encuentra errores durante la compilación; esto evita que produzca archivos dañados. Obsérvese también que ahora Inform piensa que la rutina Quijote no está siendo usada, dado que no reconoció el error tipográfico, como habría hecho un lector humano. Las advertencias se producen a veces por accidentes como estos, así que normalmente es una buena idea preocuparse de arreglar los errores primero y las advertencias después.

Números y variables

Internamente --esto es, a pesar de la apariencia externa-- todos los programas manipulan números, esencialmente. Inform entiende como "número" a un número entero entre -32768 y +32768. (Se requiere una programación especial para representar números mayores o fracciones.) Hay tres notaciones para escribir números en Inform: aquí hay un ejemplo de cada una de ellas:

    -4205
     $3f08
     $$1000111010110

La diferencia es la base en la que están expresadas. La primera están en decimal (base 10), la segunda en hexadecimal (base 16, donde los dígitos después del 9 se escriben de `a' a `f' o de `A' a `F') y la tercera en binario (base 2). Una vez que Inform ha leído un número, se olvida de qué notación fue usada: por ejemplo, si el código fuente se altera de modo que $$10110 se sustituya por 22, esto no supone ninguna diferencia en el programa producido.

Una instrucción print puede imprimir números al igual que texto, pero siempre los imprime en notación decimal normal, Por ejemplo, el programa

[ Main;
  print "El número de hoy es ", $3f08, ".^";
];

imprime

 El número de hoy es 16136.

dado que 16136 en base 10 es el mismo número que 3f08 en hexadecimal.

Inform reconoce muchas otras notaciones como "constantes", esto es, valores que están descritos específicamente en el código fuente. Una lista completa aparece más tarde, pero un caso particular es el de un solo caracter entre comillas simples. Por ejemplo

    'x'

es una constante. Un "caracter" es una sola letra o símbolo tipográfico, y todo lo que el programador necesita saber es que cada posible caracter tiene su propio valor numérico.

(!) Para la mayoría de los caracteres, este valor numérico es el valor estandar ASCII del caracter: por ejemplo, 'x' tiene un valor numérico de 120. (Esto es cierto incluso si Inform está ejecutándose en un modelo de ordenador que no utiliza normalmente el juego de caracteres ASCII). Los caracteres exóticos como '@ss' (la notación en Inform de la sz alemana) tienen códigos no estandar: véase el Z-Machine Standard Document si realmente se quiere saber más sobre esto.

Finalmente, en este grupo de notación de constantes, Inform provee dos constantes especiales:

    true
    false

que se usan para describir la verdad o la falsedad de posibles condiciones.

(!) true tiene el valor numérico 1; false tiene el valor numérico 0.

Inform tiene un concepto de "variable" como el que se usa en álgebra, donde es fácil pero limitado expresar hechos usando sólo números:

     34 - 34 = 0
     11 - 11 = 0
     694 - 694 = 0

Aunque es sugerente, esto no sirve para expresar el caso general: que cualquier número restado a sí mismo es igual a cero. Expresamos este hecho simbólicamente en álgebra escribiendo

x-x=0

donde x es una variable; esto viene a decir "para cualquier valor que tome x, esto sigue siendo verdadero".

De la misma forma, en Inform lo que podría parecer una palabra en texto puede ser una variable que representa un número; cuando el código fuente se compila, Inform no puede saber qué valor numérico representa este texto. Cuando el programa se ejecuta, siempre tendrá un valor numérico en cualquier momento dado. Si aceite_restante es una variable, la instrucción

print "Quedan ", aceite_restante, " galones.^";

se ejecuta como si aceite_restante se sustituyera por cualquiera que sea el valor actual de la variable. Más tarde, la misma instrucción podría ejecutarse de nuevo, produciendo un texto diferente porque por aquel entonces aceite_restante tiene un valor diferente.

Inform sólo puede saber que un texto (tal como aceite_restante) representa una variable si el código fuente ha "declarado" que lo es. Cada rutina puede declarar su propia selección de variables en su línea inicial. Por ejemplo, en el programa

[ Main alfa b;
    alfa = 2200;
    b = 201;
    print "Alfa es ", alfa, " mientras que b es ", b, "^";
];

la rutina Main tiene dos variables, alfa y b . Del mismo modo que la mayoría de los nombres que se dan en el código fuente (llamados "identificadores"), los nombres de variable pueden tener una longitud de hasta 32 caracteres y pueden contener letras del alfabeto, dígitos decimales o el caracter de subrayado `_' (usado a menudo para imitar un espacio). Sin embargo, a fin de evitar que se parezcan demasiado a los números, no pueden comenzar con un dígito decimal. (Así que a44 es legal pero 44a no lo es). Por ejemplo:

    turnos_por_jugar
    grafico45
    X

son todos posibles nombres de variable. Inform ignora cualquier diferencia entre mayúsculas y minúsculas en tales nombres, considerando, por ejemplo, a GRAfico45 y a graFiCO45 como el mismo nombre.

(Esp) Los nombres de variables no admiten acentos ni eñe.

Los dos signos `=' que aparecen en la rutina de más arriba son ejemplos de "operadores": notaciones realizadas normalmente con los símbolos de las teclas no alfabéticas de un teclado y que significan que algo debe hacerse con los objetos que se encuentran junto a ellos. En este contexto, `=' significa "haz que sea igual a". Cuando la instrucción alfa = 2200 se ejecuta en tiempo de ejecución, el valor actual de la variable alfa pasa a ser 2200 (y mantiene ese valor hasta que otra instrucción lo altere).

Las variables alfa y b se llaman "variables locales" porque son locales a Main ; de hecho, son su propiedad privada. El programa

[ Main alfa;
  alfa = 2200;
  Rival();
];
[ Rival;
  print alfa;
];

causa un error en la instrucción print en Rival, porque alfa no existe allí. De hecho, Rival podría incluso haber definido una variable propia llamada también alfa , y esta hubiese sido una variable separada de la otra y con un valor probablemente diferente.

Expresiones aritméticas

El lenguaje Inform es bastante rico en operadores, lo cual lo hace conciso pero no siempre muy legible. Es muy importante sentirse cómodo con los operadores, como primer paso para poder comprender adecuadamente un código fuente en Inform. Afortunadamente, estos operadores se basan en las reglas habituales para escribir fórmulas aritméticas, con lo cual resultan bastante familiares a cualquiera.

De hecho, los operadores usados con mayor frecuencia son "aritméticos": combinan uno o más números para dar como resultado otro número. Siempre que un número es requerido en una instrucción, este puede ser sustituído por una expresión que dé como resultado un número. Por ejemplo, la instrucción

segundos = 60*minutos + 3600*horas;

asigna a la variable segundos el resultado de multiplicar la variable minutos por 60 más el resultado de multiplicar la variable horas por 3600. No se necesita poner espacios en blanco entre los operadores y los "operandos" (los números con los que se trabaja): los espacios colocados a ambos lados del signo + están ahí para hacer el texto más legible.

Las operaciones aritméticas básicas se realizan con la ayuda de los operadores + (más), - (menos), * (por) y / (dividido entre).

Normalmente al dividir un entero por otro queda un resto: por ejemplo, el 3 cabe dos veces en el 7, con lo que resta 1. En notación de Inform, 7/3 da como resultado 2, 7%3 da como resultado 1.

El operador % calcula el resto tras la división, llamado simplemente "resto". No es posible hacer una división por cero, y un programa que lo intente fallará.

(!) Esto es un pequeño ejemplo de cómo Inform puede (y cómo no puede) ayudar al programador a encontrar errores. El programa

[ Main;
  print 73/0;
];

produce un error cuando se compila:

 line 2: Error: Division of constant by zero
 >   print 73/0;

dado que Inform puede ver claramente que se está intentado realizar una acción ilegal. Sin embargo, Inform no acierta a encontrar ningún error en el siguiente programa, equivalente al anterior:

[ Main x;
  x = 0;
  print 73/x;
];

y este programa compila correctamente. El archivo producido se "colgará" cuando sea ejecutado, es decir, producirá un error fatal y se parará. La moraleja es que el sólo hecho de que Inform compile un programa sin errores no garantiza que el programa haga lo que el programador pretendía que hiciese.

En una expresión compleja, el orden de los operadores puede afectar al resutado. Las dos siguientes expresiones:

    23 + 2 * 700
    2 * 700 + 23

dan como resultado 1423, porque el operador * tiene "precedencia" sobre el +, y por esto se calcula primero la multiplicación y luego la suma. Si se quiere evitar esto, se han de usar paréntesis:

    (23 + 2) * 700
    2 * (700 + 23)

dan como resultado 17500 y 1446, respectivamente. Cada operador tiene entonces un "nivel de precedencia". Cuando dos operadores tienen el mismo nivel de precedencia (por ejemplo, + y - tienen la misma precedencia), los cálculos se realizan (casi siempre) de izquierda a derecha. La notación

    a - b - c

es equivalente a

    (a - b) - c

Las reglas matemáticas estándar dan a + y - igual precedencia, menor que la de * y / (que también tienen la misma). En Inform, también el operador % tiene la misma precedencia que * y /.

El último operador púramente aritmético que queda es el "menos monario". Este se escribe como un signo menos `-', pero no significa lo mismo que una resta normal. La expresión:

    -credito

significa lo mismo que:

    0 - credito

El operador - es diferente de todos los mencionados hasta ahora porque opera con un sólo número. Tiene mayor precedencia que cualquiera de los cinco operadores "binarios" tratados de más arriba. Por ejemplo,

    -credito - 5

significa (-credito) - 5 y no -(credito -5).

Un modo de imaginar la precedencia es pensar en ella como si estuviese pegada al operador con pegamento. Un nivel superior significa un pegamento más fuerte. Así, en

    23 + 2 * 700

el pegamento alrededor de * es más fuerte que el que hay alrededor de +, así que tanto el 2 como el 700 se quedan unidos a él.

Algunos operadores no actúan simplemente sobre los valores, sino que cambian realmente el valor actual de las variables; las expresiones que los contienen se llaman "asignaciones" (porque asignan valores a la vez que trabajan con ellos). Uno de estos operadores es el símbolo igual `=':

    alfa = 72

asigna el valor 72 a la variable alfa .

Los otros dos operadores de asignación son ++ y --, que sin duda serán familiares a cualquier programador de C. Son operadores unarios, y pueden usarse de cualquiera de las siguientes maneras:

    variable++
    ++variable
    variable--
    --variable

La primera significa "lee el valor de variable, y después incrementa ese valor en uno". En ++variable el incremento ocurre antes de que se lea el valor de la variable. -- trabaja de modo similar, pero "decrementando" (reduciendo en 1) el valor de variable . Estos operadores existen como formas abreviadas, dado que se podrían conseguir los mismos efectos de otras maneras (usando sólo + y -).

Por ejemplo, supongamos que variable tiene el valor 12. Entonces tal valor sería 12, 13, 12 y 11 respectivamente en el momento en que las asignaciones anteriores lo leen, y 13, 13, 11 y 11 después de que hayan realizado los incrementos y decrementos pertinentes.

Nótese que expresiones como

    500++           (4*alfa)--      34 = beta

no tienen sentido: los valores 500 y 34 no pueden alterarse, e Inform no conoce ninguna forma de hacer que 4*alfa se decremente en uno. Todas estas expresiones producirán errores.

(!) Los "operadores a nivel de bit" se proporcionan para realizar números binarios, dígito a dígito. Esto se realiza a menudo en programas que utilizan los datos a un nivel muy bajo. *****FALTA*****

(!) Los operadores restantes se describirán más tarde a medida que sean necesarios. La tabla completa con todos ellos se encuentra en el Apéndice *.

Argumentos y valores de retorno

Como ya se ha dicho, en jerga "informita" la palabra "función" es sinónima de "rutina". Una función podría definirse como la correspondencia

(x1, ..., xn) ->f(x1, ..., xn)

donde una serie de números suministrados consituyen la entrada, y un sólo valor la salida. Estos números de entrada reciben el nombre de "argumentos". El número de salida recibe el nombre de "valor de retorno", y se dice que ha sido "devuelto" por la función.

Todas las rutinas de Inform funcionan así. Un cierto número de argumentos se suministran a la rutina cuando esta es "llamada" (es decir, cuando se la pone a funcionar) y siempre hay un único valor numérico como resultado. Algunas rutinas muy simples ocultan este hecho. Por ejemplo, considera el ejemplo Soneto:

[ Main;
  Soneto();
];
[ Soneto;
  print "Un soneto me manda hacer Violante^";
  print "que en mi vida me he visto en tal aprieto^";
];

Soneto es una rutina que no toma ningún valor como entrada (esto es, no tiene argumentos). Es un ejemplo del caso n = 0. Así que su llamada no lleva nada entre los paréntesis. Aunque de hecho devuelve un valor (este valor es true), la instrucción Soneto() simplemente llama a la rutina y desecha el valor retornado. Si Main hubiese sido

[ Main;
  print Soneto();
];

entonces el resultado al ejecutar el programa hubiese sido

 Un soneto me manda hacer Violante
 que en mi vida me he visto en tal aprieto
 1

porque a la instrucción print en Main se le ha dicho que imprima el número resultante de la llamada a Soneto.

O sea, en Inform no existe el concepto de "procedimiento" (función que no devuelve nada): cada rutina devuelve siempre un valor, aunque muchas veces este se deseche inmediatamente sin darle ningún uso.

Cuando se llama a una rutina,

    Rutina(arg1, ...)

los argumentos dados (es decir, arg1 y los que sigan) son substituídos por las primeras variables declaradas para Rutina, y luego se continúa ejecutando Rutina. Normalmente, puede haber cualquier número de argumentos desde cero hasta 7, aunque se impone un límite de 7 si se desea que Inform compile un archivo de juego de un modelo anterior (véase sección sobre limitaciones de la máquina Z).

Si la ejecución llega al marcador de final de rutina `]' de tal modo que acaba sin que se haya especificado ningún valor de retorno definido, entonces este valor es true. (Por esto es por lo que el valor de retorno de Soneto es 1: true tiene el valor de 1.)

Ejemplo 3: Cubos

Un ejemplo más típico, aunque desde luego menos estético, que Soneto:

[ Main;
  print Cubo(1), " ";
  print Cubo(2), " ";
  print Cubo(3), " ";
  print Cubo(4), " ";
  print Cubo(5), "^";
];
[ Cubo x;
  return x*x*x;
];

que, cuando es ejecutado, imprime

 1 8 27 64 125

La expresión Cubo(3) se calcula substituyendo la x por el número 3 cuando se ejecuta la rutina Cubo en ese punto; el resultado de la expresión es el número devuelto por Cubo.

Cualquier argumento que falte en la llamada a una rutina se considera cero, de modo que la llamada Cubo() es legal y produce lo mismo que Cubo(0).

Condiciones: if, true y false

Las rutinas anteriores son demasiado simples, incluso para expresar muchas funciones matemáticas, y por ello se necesitan nuevos elementos que añadan mayor flexibilidad.

Una "estructura de control" es un tipo de instrucción que controla si otras instrucciones se ejecutan o no, y si lo hacen, cuántas veces o en qué orden. De estas, la más simple es if:


if (<condición>) <instrucción>

lo cual ejecuta la <instrucción> sólo si la <condición>, en el momento en que se examina, es cierta. Por ejemplo, cuando la instrucción

if (alfa == 3) print "Hola";

se ejecuta, la palabra "Hola" se imprime sólo si la variable alfa tiene el valor 3. Es importante no confundir el operador == (examinar si algo es o no igual a otra cosa) con el operador = (de asignación de valores).

Las condiciones siempre se dan entre paréntesis. Las condiciones básicas son:

(a == b) El número a es igual al número b
(a ~= b) El número a no es igual al número b
(a >= b) a es mayor o igual a b
(a <= b) a es menor o igual a b
(a > b) a es mayor que b
(a < b) a es menor que b
(o1 in o2) El objeto o1 es poseído por o2
(o1 notin o2) El objeto o1 no es poseído por o2
(o1 has a) El objeto o1 tiene el atributo a
(o1 hasnt a) El objeto o1 no tiene el atributo a
(o1 provides m) El objeto o1 proporciona la propiedad m
(o1 ofclass c) El objeto o1 hereda la clase c

(Las condiciones referidas a objetos se explicarán más tarde). El operador especial or supone una extensión útil de este conjunto, y sirve para dar alternativas. Por ejemplo,

if (alfa == 3 or 4) print "Scott";
if (alfa ~= 5 or 7 or 9) print "Amundsen";

donde se dan dos o más valores con la palabra or entre ellos. Se imprime "Scott" si el valor de alfa es de 3 o 4, y se imprime "Amundsen" si el valor de alfa no es 5, ni 7, ni 9. or puede usarse con cualquiera de las condiciones, y se puede dar cualquier número de alternativas. Por ejemplo

if (jugador in Bosque or Pueblo or Edificio) ...

hace que el código sea mucho más claro que si escribiésemos tres condiciones separadas; o

if (x > 100 or y) ...

puede ser conveniente para determinar si x es mayor que el menor de 100 o y.

Las condiciones también se pueden construir partiendo de otras más simples (del mismo modo que las expresiones se componen de operadores simples) usando los tres operadores lógicos &&, || y ~~ (llamados "y", "o", y "no"). Por ejemplo,

if (alfa == 1 && (beta > 10 || beta < -10)) print "Lewis";
if (~~(alfa > 6)) print "Clark";

"Lewis" se imprime si alfa es igual a 1 y beta está fuera del intervalo -10, 10; "Clark" se imprime si alfa es menor o igual a 6.

Lo anterior puede llevar a pensar que las condiciones son un tipo especial de expresión que usa un cierto tipo de operadores (==, &&, or, etc.). Pero esto no es así: no son especiales, las condiciones son expresiones como cualquier otra. Es legal escribir

print (beta == 4);

por ejemplo, y el resultado será imprimir 1 si beta es igual a 4, o 0 si no. Entonces:


el resultado de una condición verdadera es 1; el resultado de una condición falsa es 0.

Esta es la razón por la que true y false se definieron como 1 y 0 respectivamente. Luego podríamos escribir código como

betaescuatro = (beta == 4);
...
if (betaescuatro == true) ...

aunque sería más sencillo escribir

betaescuatro = (beta == 4);
...
if (betaescuatro) ...

porque, del mismo modo que las condiciones pueden usarse como números, los números también pueden usarse como condiciones. Cero se considera como "falso", y todos los otros valores son considerados "verdaderos". Luego

if (1) print "Vasco";
if (0) print "da Gama";

siempre resultará en imprimir "Vasco", nunca "da Gama".

Un uso habitual de las variables consiste en utilizarlas como "banderas". Una bandera puede tener tan sólo el valor 0 o el 1, falso o verdadero dependiendo del estado del programa1. El hecho de que un número puede usarse como una condición permite la construcción de instrucciones de significado intuitivo, como

if (cavernas_inferiores_exploradas) 
   print "Ya has estado por ahí abajo.";

donde cavernas_inferiores_exploradas es una variable que el programa usa como bandera.

(!) Nótese que && y || sólo calculan aquello que es absolutamente necesario para determinar el valor de verdad. Esto es,
    if (A && B) ...

calculará primero A. Si esta es falsa, no hay necesidad de calcular B, y de hecho nunca se la calculará. Sólo si A es verdadera se calcula también B. Esto sólo importa cuando se esté trabajando con algunos casos como

    if (x == 7 && Rutina (5)) ...  

donde puede ser importante saber que la Rutina nunca es llamada si x tiene un valor distinto de 7.

Ejemplo 4: Factoriales

El factorial de un entero positivo n se define como el producto de 1 ×2 ×3 ×...×n, de modo que, por ejemplo, el factorial de 4 es 24. Aquí hay una rutina de Inform que calcula factoriales:

[ Main;
  print Factorial(7), "^";
];
[ Factorial n;
  if (n==1) return 1;
  return n*Factorial(n-1);
];

Esto calcula el factorial de 7 y da como resultado 5040. (Los factoriales crecen rápidamente y el factorial de 8 ya es demasiado grande como para que "quepa" en un número estandar de Inform, así que llamar a Factorial(8) daría una respuesta errónea)2.

La rutina Factorial se llama aquí a sí misma: esto se llama "recursión". La ejecución alcanza una "profundidad de siete rutinas" antes de empezar a "emerger". Cada una de estas copias de Factorial se ejecuta con su propio valor privado de la variable n.

El truco de la recursión puede ser peligroso. Si uno llama a la rutina

[Desastre;
  return Desastre();
];

entonces, a pesar de la tranquilizadora presencia de la palabra return, la ejecución continúa infinitamente, incapaz de acabar evaluando el valor de retorno. La primera llamada a Desastre necesita hacer una segunda antes de que pueda terminar; la segunda necesita hacer una tercera; y así sucesivamente. Esto es un ejemplo de error de programación que será desastroso cuando el programa se ejecute, aunque no causará ningún error cuando el fuente se compile. (Se ha probado que es imposible construir un compilador capaz de detectar este tipo general de error. Inform ni siquiera lo intenta).

Bloques de código, else y switch

Una característica común de todas las estructuras de control es que en lugar de dar simplemente una <instrucción>, uno puede dar una lista de instrucciones agrupadas en una sola unidad llamada "bloque de código". Tal agrupamiento comienza con una llave abierta `{' y termina con una llave cerrada `}'. Por ejemplo,

if (alfa > 5)
{   print "El cuadrado de alfa es ";
    print alfa*alfa;
    print ".^";
}

Si alfa es 3, no se imprime nada; si alfa es 9, se imprime

 El cuadrado de alfa es 81.

(Como viene siendo habitual, el formateo es una cuestión de convención; normalmente se escriben los bloques de código con un indentado de varios espacios). En algunos aspectos, los bloques de código son como las rutinas, y al principio puede parecer inconsistente el escribir las rutinas entre corchetes (`[' y `]') y los bloques de código entre llaves (`{' y `}'). Sin embargo, los bloques de código no pueden tener variables privadas propias y no devuelven valores, y es posible que la ejecución se salga fuera de los bloques de código, o que salte de uno a otro, cosa que no puede suceder con las rutinas.

Una instrucción if puede tener opcionalmente la forma


if (<condición>) <instrucción1> else <instrucción2>

de tal modo que la <instrucción1> se ejecutará si la condición es verdadera, y la <instrucción2> si es falsa. Por ejemplo,

if (alfa == 5) print "Cinco."; else print "No cinco.";

Nótese que la condición sólo es evaluada una vez. La instrucción

if (alfa == 5)
{   
    print "Cinco.";
    alfa = 10;
}
else print "No cinco.";

no puede imprimir nunca "Cinco." y después "No cinco.".

La cláusula else tiene una pega: el problema de los "elses colgantes".

if (alfa == 1)
    if (beta == 2)
        print "Claramente si alfa=1 y beta=2.^";
    else
        print "Ambiguo.^";

es ambiguo en cuanto a qué instrucción if acompaña el else. La respuesta (en Inform 6, aunque esto ha cambiado desde otras versiones anteriores del lenguaje) es que un else siempre acompaña a su if más cercano, a menos que haya llaves que indiquen lo contrario. Luego el else de arriba está emparejado con la condición beta, no con la condición alfa.

En cualquier caso, es mucho más seguro usar llaves para expresar lo que se quiere decir, como en:

if (alfa == 1)
{   
    if (beta == 2)
        print "Claramente si alfa=1 y beta=2.^";
    else
        print "Claramente si alfa=1 pero beta no es 2.^";
}

El constructo if...else... es ideal en los casos en que la ejecución debe elegir dos posibles "caminos", como si fueran señales ferroviarias, pero resulta incómodo cuando hay más de dos posibles resultados. Continuando con la analogía, el constructo switch es como la tornavía de un ferrocarril.

print "El tren del andén 1 va a ";
switch(DestinoEnAnden(1))
{   1: print "Madrid.";
    2: print "Ourense.";
    3: print "A Coruña.";
}

Cada posible valor debe ser una constante, así que

switch(alfa)
{   beta: print "¡Las variables alfa y beta son iguales!";
}

es ilegal.

Se pueden especificar cualquier número de opciones, y los valores pueden agruparse en una sola opción. Por ejemplo,

print "La misión STS-", num, " se voló en el transbordador espacial";
switch(num)
{   1 to 5, 9: print " Columbia.";
    6 to 8:    print " Challenger.";
    10 to 25:  if (num == 12) print " Discovery";
               print ", pero se le dio el número de vuelo 51-B.";
    default:   print ".";
}

imprimirá una frase verdadera (mientras num esté entre 1 y, en el momento en que esto se escribe, 78), aunque puede que incompleta. La cláusula default se ejecuta si la expresión original no es igual a ninguno de los otros valores, y siempre debe ponerse al final, si es que se pone. En este caso, significa que si num es 62, por ejemplo, entonces se imprimirá

 La misión STS-62 se voló en el transbordador espacial.

Nótese que cada una de las cláusulas es por sí misma un bloque de código y no necesita llaves { y } alrededor de ella para delimitarla del resto de la rutina: esta abreviación hace que la instrucción switch sea mucho más legible3

while, do...until, for, break, continue

Las otras cuatro estructuras de control de Inform son todas "bucles", esto es, formas de repetir la ejecución de uns instrucción dada (o bloque de código). La explicación de una de ellas, (objectloop), se retrasará hasta la sección *.

Las dos formas básicas de bucle son while y do...until:


while (<condición>) <instrucción>
do <instrucción> until (<condición>)

La primera chequea repetidamente la condición y, siempre que sea verdadera, ejecuta la instrucción. (Si la condición no es verdadera la primera vez que se chequea, la instrucción nunca se ejecuta). Por ejemplo:

[ RaizCuadrada n;
  x = n;
  while (x*x >n) x=x-1;
  return x;
];

es un método (bastante habitual) de encontrar raíces cuadradas. (Si se llama a RaizCuadrada(200), entonces x va pasando por los valores 200, 199, ..., 14, momento en el cual x*x <= n, dado que 14×14=196.)

El bucle do...until repite las intrucciones dadas hasta que la condición es encontrada verdadera. (Pero, ya que la condición no se evalúa hasta despuñes de haber ejecutado la instrucción, la instrucción se ejecutará como mínimo una vez).

Un tipo particualar de bucle while se usa tan a menudo que hay una abreviación para él, llamada for. Por ejemplo,

contador = 1;
while (contador <= 10)
{   print contador, " ";
    contadador++;
}

produce el resultado

 1 2 3 4 5 6 7 8 9 10  

(Hay que recordar que contador++ añade 1 a la variable contador). Los lenguajes como BASIC hacen un uso extensivo de este tipo de bucle. Por ejemplo, en BBC BASIC, el bucle anterior se escribiría

FOR contador = 1 TO 10
        PRINT contador;" ";
NEXT

NEXT es una palabra que (de forma algo inadecuada) significa "el bloque de código termina aquí", y es por tanto equivalente a la } de Inform. Todo lo anterior se usa para significar "para valores del contador que vayan de 1 a 10, haz...", de ahí la elección de la palabra FOR

Inform (al igual que el lenguaje C) usa una sintaxis más flexible que esta, pero que continúa llamándose for. Puede producir cualquier bucle de la forma


<comienzo>
while (<condición>)
{ ...
<actualización>
}

donde <comienzo> y <actualización> son instrucciones de asignación. La notación para hacer lo mismo con for sería4:


for (<comienzo> : <condición> : <actualización>) ...

Por ejemplo, el bucle descrito más arriba se consigue con

for (contador=1 : contador<=10 : contador++)
     print contador, " ";

Obsérvese que si la condición es falsa la primera vez, el bucle nunca se ejecuta. Por ejemplo,

for (contador=1 : contador<0 : contador++)
     print "Plátano";

no imprime nada.

(!) En este punto cabe mencionar que en Inform se pueden combinar varias asignaciones en una sola, separándolas con comas. Por ejemplo,

   i++, puntuacion=50, j++

(tres asignaciones separadas por comas) es una sola instrucción. Esto nunca resulta útil en el código normal, ya que las asignaciones pueden separarse por punto y coma como es habitual. Sin embargo, sí es útil dentro de bucles for:

for (i=1, j=5: i<=5: i++, j--) print i, " ", j, ", ";

produce el resultado "1 5, 2 4, 3 3 , 4 2, 5 1,".

Cualquiera de las tres partes de una instrucción for puede omitirse. Si se omite la condición, se asume que siempre será verdadera, o sea, no hay ningún chequeo que realizar para ver si el bucle debería terminar, y de esta forma el bucle continúa infinitamente.

A simple vista, los siguientes bucles se repiten todos para siempre:


while (true) <instrucción>
do <instrucción> until (false)
for (::) <instrucción>

Pero siempre hay una salida. Una forma consiste en colocar un return dentro del bloque <instrucción>. Otra es saltar a una etiqueta fuera del bucle (jump, que es la instrucción de salto, se explicará en la sección *). Pero lo más elegante es usar la instrucción break, que hace que la ejecución "se salga" del bucle o de una instrucción switch, y continúe después de la llave que delimita el bloque: puede considerarse como un "final prematuro". Todas estas formas son totalmente "seguras", y no hay ningún peligro en dejar un bucle a medio hacer.

La otra instrucció simple utilizada dentro de los bucles es continue. Esta causa que la iteración actual cese inmediatamente, pero no termina el bucle entero. En lugar de eso, se salta de nuevo al principio del bucle, para comenzar una iteración nueva, dejando sin ejecutar lo que había después del continue. Por ejemplo,

for (i=1: i<=5: i++)
{   if (i==3) continue;
    print i, " ";
}

imprimirá "1 2 4 5" (saltándose el 3).

Ejemplo 5: Un puzzle numérico

La rutina EjecutaPuzzle es un ejemplo interesante de bucle que, aunque aparentemente bastante simple, contiene una trampa para los incautos:

[ EjecutaPuzzle n contador;

   do
   {   print n, " ";
         n = SiguienteNumero(n);
         contador++;
   }
   until (n==1);
   print "1^(hemos necesitado ", contador, "pasos para llegar al 1)^";
];
[SiguienteNumero n;
   if (n%2 == 0) return n/2;    ! Si n es par, divídelo a la mitad
   return 3*n + 1;              ! Si n is impar, tripícalo y añádele 1
];

La llamada EjecutaPuzzle(10), por ejemplo, produce el siguiente resultado:

 10 5 16 8 4 2 1
 (hemos necesitado 6 pasos para llegar al 1)

El código fuente asume que, sin importar cuál es el valor inicial de n, acabaremos por llegar a 1 con el suficiente número de iteraciones. Si esto no sucediera, el programa quedaría bloqueado por un bucle infinito, imprimiendo números infinitamente.

La rutina es aparentemente muy simple, así que parece muy razonable pensar que, si nos ponemos a ello, deberíamos ser capaces de decidir si es o no "segura" de usar (o sea, que podríamos garantizar que terminará siempre, o no).

Y aún así, nadie sabe si esta rutina es "segura". La hipótesis de que cualquier valor de n acabará finalmente por llegar al valor 1 tiene al menos cincuenta años de antigüedad, pero aún no ha sido probada, habiendo resistido todos los "ataques" matemáticos. (Alarmantemente, a EjecutaPuzzle(27) le lleva 111 iteraciones alcanzar el valor de 1.)

quit, jump, y el estado del programa

Quedan todavía cuatro instrucciones que controlan el flujo de la ejecución. quit finaliza el programa inmediatamente (como si se hubiera producido un valor de retorno en la rutina Main). Esta medida tan drástica debería ser reservada para aquellos puntos de un programa en los que se han detectado errores tan tremendos que no tiene sentido seguir con ellos. Mejor aún sería no usarla nunca5.

La instrucción jump transfiere la ejecución a algún otro lugar dentro de la misma rutina. El lugar en cuestión es identificado con un "nombre" que el programador decide. (Algunos lenguajes de programación la llaman goto. Esta instrucción se puede usar y se ha usado de formas muy feas y poco elegantes, por lo que la construcción en sí misma fue acusada hace tiempo de ser una vulgaridad informática que conduce al pecado a los que la usan. Un buen uso de las estructuras de control evitará casi siempre la necesidad de usar la instrucción jump y resultará en programas más legibles. Pero el pecado es universal...).

Para usar jump, se necesita una notación para marcar algunas partes determinadas del código fuente. Tales marcadores reciben el nombre de "etiquetas". Por ejemplo:

[ Main i;
  i=1;
  .Marcador;
  print "He imprimido esto ", i++, " veces.^";
  jump Marcador;
];

Este programa tiene una etiqueta, Marcador. Una instrucción que consista sólo en un punto y luego un identificador significa "pon una etiqueta aquí y llámala así".

(!)(!) Los programas en Inform tienen la habilidad de guardar "instantáneas" de su propio estado y de devolverse a sí mismos a ese estado anterior. Esta instantánea incluye valores de variables, el punto donde se está ejecutando el código, etc. Del mismo modo que no podemos saber si el universo tiene solo seis mil años de antigüedad, como afirman los creacionistas, sino que ha sido creado hace poco, junto con un buen montón de fósiles cuidadosamente falsificados por Dios para hacernos creer que es más antiguo, un programa en Inform no puede saber si ha estado ejecutándose todo el rato o si ha sido reiniciado recientemente. Las instrucciones necesarias son save y restore:

save <etiqueta>
restore <etiqueta>

Este es un raro ejemplo de una característica de Inform que puede depender del "estado de salud" de la máquina en la que se ejecute: por ejemplo, si todo el espacio en disco está lleno, save fallará. El programador debería tener siempre presente que estas instrucciones bien podrían fallar. Si la operación ha sido exitosa, terminará con un jump a la etiqueta indicada. (En realidad, la <etiqueta> para restore es ignorada, ya que, si todo ha ido bien, la ejecución continuará con el estado que tenía el programa cuando hizo el save. Por tanto continuará en la <etiqueta> señalada por el save. Solo si restore fracasa, la ejecución continuará en la instrucción siguente a restore).

Imprimiendo resultados (output)

Cuando se imprime un texto, normalmente cada caracter se imprime exactamente como se especificó en el código fuente. Hay cuatro caracteres, sin embargo, que tienen significados especiales. Como ya se ha explicado, ^ significa "imprime un retorno de carro". El caracter ~, que significa "imprime unas comillas", es necesario dado que las comillas normales terminarían la cadena de texto a imprimir. Así,

        
  "~Mira~, dice Pedro. ~Los calcetines pueden saltar.~^Juana está de
  acuerdo."

se imprime como

 "Mira", dice Pedro. "Los calcetines pueden saltar."
 Juana está de acuerdo.

El tercer caracter especial que queda es @, que se usa para los caracteres acentuados y otros efectos inusuales, como se describe más abajo6. Finalmente, \ se reservaba para indicar que la cadena no termina en esta línea de código, sino que continúa en la siguiente. Esto ya no es necesario en las nuevas versiones de Inform, pero se mantiene para que los programas antiguos todavía funcionen. Si deseas imprimir los símbolos ~, ^, @ o \, sigue leyendo.

El texto entrecomillado puede ocupar más de una línea. Cuando Inform lee una instrucción como esta

print "Muchos dicen mal de mí,
       y yo digo mal de muchos;
       mi decir es más valiente,
       por ser tantos y ser uno.";

los finales de línea son reemplazados por un espacio (y todos los espacios que hay al principio de la siguiente línea, son ignorados). Por tanto, el texto que imprimiría sería: "Muchos dicen mal de mí, y yo digo mal de muchos; mi decir es más valiente, por ser tantos y ser uno." (Hay una excepción: si el último carácter de la línea es ^, entonces no se sustituye el salto de línea por un espacio, sino que se ignora sin más. La siguiente línea impresa comenzaría con el primer carácter distinto de blanco que hay en la siguiente línea de la cadena).

Hasta ahora, para imprimir cosas sólo hemos usado la orden print, tanto para imprimir números como cadenas (esto es, trozos de texto encerrados entre comillas dobles "). Puesto que Inform es básicamente un lenguaje para escribir aventuras conversacionales, lo suyo es el texto, y proporciona unas cuantas utilidades más para imprimir.

    new_line

es un comando que simplemente imprime una línea nueva (tambíen conocida como retorno de carro, como si hubieramos empujado la palanca de una máquina de escribir mecánica, para moverla de nuevo al margen izquierdo y hacerla avanzar una línea). Es equivalente a

    print "^"

pero es una abreviatura cómoda. De forma análoga,


spaces <número>

imprime una secuencia de espacios, donde <número> representa la cantidad de espacios a imprimir.


inversion

imprime el número de versión del compilador Inform que se ha usado para compilar el programa (por ejemplo, podría imprimir "6.15").


box <cadena_1> ... <cadena_n>

muestra una caja en video inverso, centrada en la pantalla, que contiene cada una de las cadenas en una línea separada. Suele usarse para hacer aparecer citas literarias. Por ejemplo:

box "Passio domini nostri" "Jesu Christi Secundum" "Joannem";

sacaría en un rectángulo de vídeo inverso:

 Passio domini nostri
 Jesu Christi Secundum
 Joannem

(el comienzo del libreto de Arvo Pärts "La Pasión de San Juan").

Normalmente el texto se muestra en un tipo de letra "normal" (o "Roman"). El aspecto exacto que tendrá, dependerá de la máquina en que se ejecute. En muchas máquinas (por ejemplo, al ejecutarse bajo WinFrotz o IznoguZ), se usará un tipo de letra "proporcional", también llamado "de ancho variable", como la letra del manual que estás leyendo. En este tipo de letra una "w" es más ancha que una "i". Este texto es mucho más cómodo de leer para el ojo, pero hace muy difícil imprimir diagramas construidos a base de texto. Por ejemplo, la instrucción:

print "+-----------+
      ^+   Hola    +
      ^+-----------+^";

imprimirá algo bastante irregular, si en el tipo de letra usado los caracteres "-", "+" y " " (espacio) no tienen exactamente el mismo ancho. Pero a veces querrás hacer un diagrama así (para representar un mapa esquemático, por ejemplo, o imprimir una tabla). Por ello existe la instrucción font:

    font on
    font off

La instrucción font off cambia el tipo de letra a otro que tenga ancho fijo (como por ejemplo el tipo de letra courier); la instrucción font on vuelve al tipo de letra original. Aparte de esto, hay unos pocos tipos de letra más:

    style roman  

cambia a un tipo de letra "Roman" recto (el tipo por defecto). También hay:

    style bold
    style underline
    style reverse

Estos estilos hacen que las letras salgan con diferente aspecto en pantalla, aunque el aspecto concreto depende del intérprete y del sistema operativo que estés usando. El estilo bold debe sacar un tipo de letra "destacado" de alguna forma. En intérpretes que tengan acceso a diferentes tipos de letra (como WinFrotz, por ejemplo), usarán un tipo en negrita para esto. En otros intérpretes sin acceso a tipos de letra (como DOS Frotz), el texto saldrá de otro color más brillante.

El estilo underline es para remarcar el texto de alguna forma. Según el intérprete, puede aparecer subrayado, o bien en cursiva, o bien simplemente de otro color más llamativo.

Finalmente, reverse indica que se intercambien el color de fondo y el de la letra, por ejemplo, letras azules sobre fondo amarillo, si el texto normal aparece en letras amarillas sobre fondo azul.

Inform puede usarse para hablar idiomas distintos del inglés: hay prueba de ello en italiano, alemán, francés y español. Para estos idiomas es necesario incorporar letras acentuadas, y para ello se usa el carácter "escape" @. En las últimas versiones del compilador (a partir de la 6.15), los acentos se pueden introducir también "directamente", siempre que se use un editor basado en la codificación ISO-Latin17.

Si, por cualquier razón, no puedes usar letras acentuadas en tu ordenador, aún así puedes escribir juegos que usen acentos en su salida, gracias al caracter de escape @. Detrás de @ pones un símbolo que indica el tipo de acento deseado, y seguidamente la letra que recibirá el acento. Estas son las opciones:

@^ pone un circunflejo sobre la letra siguiente: a,e,i,o,u,A,E,I,O,U
@' pone un acento agudo en la letra siguiente: a,e,i,o,u,y,A,E,I,O,U,Y
@' pone un acento grave en la letra siguiente: a,e,i,o,u,A,E,I,O,U
@: pone una diéresis en la letra siguiente: a,e,i,o,u,A,E,I,O,U
@c pone una cedilla en la letra siguiente: c, C
@~ pone una virgulilla en la letra siguiente: a,n,o,A,N,O
@\ pone una barra cruzando la letra siguiente: o,O
@o pone un anillo sobre la letra siguiente: a,A

Además de los acentos, hay otras combinaciones para producir símbolos especiales:

@ss La ß alemana
@<< Comillas españolas
@>>  
@ae ligaduras
@AE  
@oe  
@OE  
@th Acentos de islandia
@et  
@Th  
@Et  
@LL símbolo de la libra
@!! Abrir admiración española
@?? Abrir interrogación española

Por ejemplo:

print "Les @oeuvres d'@Aesop en fran@ccais, mon @'el@`eve!";
print "Na@:ive readers of the New Yorker will re@:elect Mr Clinton.";
print "Carl Gau@ss demostr@'o el Teorema Fundamental del @'Algebra.";

Si tu teclado puede producir esos símbolos, entonces no necesitas usar estos molestos códigos de escape, sino que puedes usar los símbolos directamente. Así, por ejemplo:

print "Les @oeuvres d'@Aesop en français, mon élève!";
print "Naïve readers of the New Yorker will reëlect Mr Clinton.";
print "Carl Gauß demostró el Teorema Fundamental del Álgebra.";

(!) El ecape @ tiene otros dos usos. Uno de ellos sirve para evitar el problema de que es imposible imprimir un "@". Dos @ seguidas y un número detrás, representa al carácter cuyo código es ese número. Los casos más útiles son:
@@92 Produce \
@@64 Produce @
@@94 Produce ^
@@126 Produce ~
lo que nos permite imprimir estos caracteres que no pueden usarse directamente por tener otros significados.

(!)(!) El segundo uso es mucho más oscuro, y bastante poco útil. Inform mantiene una lista con 32 pseudo-variables, en las que puede almacenar texto. Estas variables no tienen nombre, sino simplemente un número de 0 a 31 para indentificarlas.

@00 dentro de una cadena de texto, es sustituida por el valor de la cadena 0, @01 por el de la cadena 1, etc... @31 corresponde a la última cadena.

Los valores de esas cadenas pueden asignarse con la instrucción string:

string 0 "seta venenosa";

asignaría el texto "seta venenosa" a la cadena número 0. Hay razones técnicas por las que la asignación debe utilizar un texto literal, y no el valor almacenado en otra variable.

Finalmente, es hora de hablar de print. Hay dos formas: print y print_ret. La única diferencia es que la segunda forma imprime un retorno de carro al final del texto y causa un retorno de la rutina que se está ejecutando en ese momento, retornando el valor true. Por tanto print_ret debe leerse como "imprime y después retorna". La instrucción

print_ret "Ya está bien.";  

es equivalente a

print "Ya está bien.^"; rtrue;  

De hecho, aún puede abreviarse más, y escribirse sin poner siquiera print_ret:

"Ya está bien.";

Aunque los recién llegados a Inform a menudo se lían con el hecho de que esta instrucción de aspecto inocente, cause en realidad unr retorno de la rutina, la verdad es que es una abreviatura muy útil que compensa realmente cuando se escriben aventuras conversacionales. Observa que al compilar el progrma:

[ Main;
 "Hola, vamos a imprimir un número...";
  print 45*764;
];

Inform producirá el siguiente aviso:

line 3: Warning: This statement can never be reached.
 >   print 45*764;

debido a que la primera cadena que hay dentro de Main es en realidad un print_ret abreviado, de modo que el texto es impreso, después se imprime un retorno de carro, e inmediatamente se retorna de la función Main. Como el mensaje de aviso nos indica, no hay forma de que la línea 3 pueda ser ejecutada.

¿Qué podemos imprimir? La respuesta es: "una lista de términos separados por comas". Por ejemplo el print:

print "El valor es ", valor, ".";  

contiene tres términos. Un término puede ser una de las cosas siguientes:
<una cantidad numérica> Se imprime como un número en decimal, con signo
<texto entre comillas> Se imprime el texto
(<regla>) <cantidad> La cantidad se imprime según una cierta regla

Inform proporciona una serie de reglas preparadas, y también permite que el programador cree reglas nuevas. Las reglas más importantes son:
(char) imprime el carácter cuyo código es <cantidad>
(string) imprime el texto representado por <cantidad>
(address) imprime la palabra que hay en esa dirección

La última se usa raramente, y sólo para imprimir palabras del diccionario del juego.

(!) La forma print (string) ... necesita una pequeña explicación. El programa

x="¡Hola!";
print (string) x;    

imprime el texto "¡Hola!". Mientras que

x="¡Hola!";
print x;

lo que imprime es un número misterioso. Esto se debe a que internamente las cadenas de texto son representadas por números8.

Las restantes reglas proporcionadas por el lenguaje son para usar juntocon la Librería, y se detallan en la capítulo sobre descripción de objetos. Resumiendo brevemente:
(the) Imprime un artículo definido y el nombre del objeto
(The) Lo mismo, pero con la inicial en mayúsculas
(name) Lo mismo, pero sin artículo
(a) Lo mismo, pero con un artículo indefinido
(number) Escribe el número con palabras
(property) (para depuración) imprime el nombre de esa propiedad
(object) (para depuración) imprime el nombre interno del objeto

Observa que (the) en minúsculas es diferente a (The) en mayúsculas. ¡Esto es bastante inusual!. Los nombres de directivas, y de variables pueden mezclar mayúsculas y minúsculas, porque no tiene efecto. RaNa significa lo mismo que rana. En cambio las palabras reservadas del Inform como print o (name) tienen que estar en minúsculas (con la excepción de (The)).

Para crear una regla nueva, simplemente escribe una rutina con ese nombre. La regla ya puede ser usada en un print poniéndola entre paréntesis.

(Esp) Para hacer más fácil el trabajo al programador español, la librería InformATE define las reglas (el), (un), etc... Sin embargo, estas reglas no son parte de Inform, sino que están definidas en la librería. Debido a ello, existen algunas limitaciones:
  • No es posible crear una regla llamada (El), pues, al tratarse de una regla de usuario y no del lenguaje, las mayusculas no tienen efecto, y esa regla sería la misma que (el). Es por esto que la regla para imprimir un artículo definido en mayúsculas se llama (_El). El subrayado inicial sirve para distinguirla de (el).
  • No es posible crear una regla llamada (nombre), porque nombre ya se está usando como nombre de propiedad, por tanto no puede ser el nombre de una regla ni de ninguna otra cosa. Es por esto que la traducción de (name) en InformATE es (_nombre_).

Ejemplo 6: Imprimir un número en hexadecimal

Gracias a las dos rutinas siguientes, podemos imprimir un número en forma hexadecimal sin signo. Se usarían así:

print (hex) 16339;

lo que producirá la salida "3fd3". Las funciones serían estas:

[ hex x y;
  y = (x & $ff00) / $100;
  x = x & $ff;
  print (hdigit) y/$10, (hdigit) y, (hdigit) x/$10, (hdigit) x;
];
[ hdigit x;
  x = x % $10;
  if (x<10) print x; else print (char) 'a'+x-10;
];

Una vez que se han definido estas rutinas, (hex) y (hdigit) pueden usarse en cualquier lugar del programa como nuevas reglas para print.

Funciones internas 1: random e indirect

Inform proporciona un pequeño conjunto de funciones pre-programadas internamente pero que se usan en la misma forma que las funciones programadas por el usuario. Todas ellas, excepto 2, son para el manejo de objetos y se tratan en la sección sobre objetos. Explicaremos aquí las otras dos: random e indirect.

random tiene dos formas. La primera:

    random(N)

devuelve un número elegido al azar entre 1, 2, ...N. La cantidad N tiene que ser siempre positiva, y menor de 32767 para que la rutina funcione correctamente. La segunda forma:

    random(cantidad1, cantidad2, ...)

devuelve una de esas cantidades, elegida al azar de forma uniforme. Por tanto la instrucción

print (string) random("rojo", "verde", "azul", "amarillo", "naranja");  

imprimirá aleatoriamente uno de esos colores, todos ellos con la misma probabilidad de ser elegidos. Análogamente

print random(13,17);

tiene un 50% de probabilidades de imprimir 13, y otro 50% de imprimir 17.

(!)(!) La otra función interna es indirect, que tiene un uso bastante avanzado. La instrucción:
    indirect(funcion, arg1, arg2, ...)    

llama a esa funcíón pasándole esos argumentos. Por tanto equivale a:

    funcion(arg1, arg2, ...)

pero tiene la virtud adicional de que funcion no tiene por qué ser dado como un nombre de función literal, sino que puede ser el resultado de algún cálculo. Por ejemplo:

indirect(random(RutinaUno, RutinaDos), 45);

tiene un 50% de probabilidades de llamar a RutinaUno(45), y un 50% de llamar a RutinaDos(45).

indirect debe ser usada con cuidado. Si lo que se le pasa como primer argumento no es la dirección de ninguna función válida, el programa romperá estrepitosamente.

Leyendo datos

(!)(!)

El programador de InformATE raramente necesitará leer datos del teclado en la práctica, ya que en todas las situaciones del juego las rutinas de parsing de la librería se ocuparán de ello. Sin embargo, por completar esta sección, se trata aquí la instrucción read que es la principal vía para leer datos del teclado. Probablemente este apartado no tenga mucho sentido para los lectores que no hayan leido el resto del manual.

La sintaxis es


read <array de texto> <buffer de parsing> <rutina>

donde la <rutina> es opcional. Si se proporciona una, se la llamará justo antes de que se efectúe la lectura del teclado. Es habitual poner ahi una rutina que actualice la "linea de estado" del juego (las primeras líneas de la pantalla).

Lo que hace esa instrucción es leer una línea de texto (es decir, espera hasta que el usuario haya terminado de teclear una línea y haya pulsado Intro), copia el texto que el usuario ha escrito en el array <array de texto> y después intenta comprender lo que se ha escrito (mirando si las palabras individuales están en el diccionario del juego), dejando los resultados de esta "comprensión" en el array <buffer de parsing>.

Antes de ejecutar esta instrucción, el array de texto ha tenido que ser inicializado, indicando el máximo número de letras que se van a aceptar. Este número debe estar almacenado en el elemento 0 del <array de texto>. Una vez que read haya finalizado, dejará en el elemento 1 de ese mismo array el número de caracteres realmente leidos, y los caracteres en cuestión estarán a partir del elemento 2. Por tanto:

array_de_texto -> 0 = 60;
read array_de_texto 0;
for (n = 0: n< array_de_texto->1: n++) print (char) array_de_texto->(n+2);
new_line;

leerá una línea de 60 caracteres como máximo, y después la imprimirá en pantalla. (El array array_de_texto tiene que haber sido creado previamente, así como la variable local n, por supuesto.)

Observa que en el ejemplo anterior no hemos especificado buffer de parsing (hemos puesto 0 en su lugar. Esto causa que read no intente interpretar el texto leido. Si en vez de 0 hubiesemos dado la dirección de un array, entonces read habría analizado el texto, separandolo en palabras y tratando de encontrar cada una de las palabras en el diccionario del juego. Ver la sección sobre estructuras de datos del parser para más detalles.


Zak McKraken - spinf@geocities.com

El lenguaje de los DatosInform como lenguaje de programaciónInform como lenguaje de programaciónEl lenguaje de las Rutinas