![]() | ![]() | ![]() | Cómo se interpretan los nombres |
Poner Nombre a los Gatos es un tema complejo,
No es un juego de niños, no es un pasatiempo;
Seguro que piensas soy un hombre demente
Si digo que el gato ha TRES NOMBRES DIFERENTES
T.S.Eliot (1888-1965), The Naming of Cats
Supón que tenemos un tomate, definido con el nombre:
nombre 'tomate' 'verde' 'frito', |
pero que más adelante madura, y el jugador debe poder referirse a él como "tomate rojo". La lista de nombres de un objeto no es más que un array, por lo que es perfectamente posible consultar lo que contiene e incluso alterarlo. Por ejemplo, para consultarlo:
[ Nombres obj i;
for (i=0: i<2*obj.#nombre: i++)
print (address) (obj.&nombre)-->i, "^";
];
|
y para alterarlo:
(tomate.&nombre)-->1='rojo'; |
pero no es una solución muy flexible ni elegante. Es hora de empezar a trastear con el parser.
![]() |
Observa que lo que no podemos hacer es cambiar el tamaño de un
array. Si necesitamos "añadir" nombres a la propiedad nombre,
debemos de declararla de antemano con el sitio suficiente, es decir,
podemos poner en ella 30 copias de una palabra inescribible, como
'nada.' (nota el punto en la palabra).
|
El parser de InformATE está diseñado para ser de acceso lo más abierto posible, porque no puede existir un parser lo bastante general como para servir para cualquier juego, a menos que sea altamente modificable. Lo primero que hace el parser es leer texto del teclado y separarlo en una secuencia de palabras, de modo que el texto "hombre pobre, come el pan gris" se convierte en la secuencia:
hombre / pobre / , / come / el / pan / gris
y estas palabras se numeran comenzando por 1 (que sería la
palabra hombre).
![]() |
La librería InformATE, además de separar las palabras basándose en
los espacios y los signos de puntuación, en ocasiones rompe un verbo
en dos palabras, para separar el pronombre que puede llevar
adosado. Así, la orden "EXAMINATE", es convertida en la secuencia
de palabras:
examina / -te('-te' es un nombre que siempre es interpretado como "el jugador"). Pero no siempre separa el sufijo. La regla es:
Esta regla funciona bien en la mayoría de los casos, pero puede dar resultados inesperados a veces, por eso conviene conocerla. |
El parser mantiene en todo momento un indicador de qué palabra está
examinando dentro de la secuencia anterior. Este indicador se denomina
"número de palabra" y está almacenado en la variable global np. Si
llamamos a la rutina SiguientePalabra(), obtenemos la palabra de
diccionario que hace el lugar np dentro de la secuencia (recordemos
que la primera hace el lugar 1), y además np es incrementado. Por
ejemplo, supongamos que el parser está examinando la palabra 6 del
ejemplo anterior (pan), intentando resolver si esa palabra y las
siguientes pueden ser el nombre de un objeto del juego. Si en ese
momento se efectúa una llamada a SiguientePalabra(), ésta retornará
la palabra 'pan'. Llamándola de nuevo retornará 'gris'.
Conviene recordar que si el jugador ha tecleado equivocadamente 'pna
gris', entonces SiguientePalabra() retornaría 0, que significa
"palabra no reconocida" (asumiendo que el juego no usa la palabra
'pna' en ningún otro punto del código fuente. Basta que el código
incluya una línea como if (w=='pna') para que la palabra 'pna'
pase al diccionario automáticamente).
El diccionario del juego tiene una resolución de sólo 9 letras (menos aún, 6, si Inform está compilando para la versión 3 de la máquina Z, pero esta es una versión obsoleta que ya no se usa. De hecho, la librería InformATE sería demasiado grande para esa versión). Por tanto, las palabras 'polisaturar' y 'polisaturado' serían iguales para Inform. Además, no se hace distinción entre mayúsculas y minúsculas. Se permite que las palabras contengan números o símbolos, y también que contengan acentos, pero se desaconseja esto último.
![]() |
Si usas una palabra con acentos como 'camión', entonces el usuario
debe escribir exactamente esa palabra para que el parser la
reconozca. Si el usuario pone 'camion' (sin acento), el parser no la
reconocerá. Esto puede ser una faena para los usuarios que no pueden
usar acentos (recuerda que Inform es multiplataforma, y algunos
intérpretes no permiten introducir acentos).
Sin embargo, si defines la palabra como 'camion' (sin acento), nadie sale perjudicado, porque el parser entenderá tanto 'camion' como 'camión'. Esto último se debe a que, si el parser no entiende la palabra a la primera, le quita los acentos y vuelve a buscarla en el diccionario. De modo que 'camión' no es encontrado (porque tu juego no define esa palabra), pero 'camion' (una vez quitado el acento por el preparser) sí será encontrado.
|
![]() ![]() |
Una palabra de diccionario puede incluso contener espacios, puntos o
comas en su interior. En ese caso es "inescribible". Por ejemplo,
la palabra de diccionario 'in,out' es inescribible porque si el
jugador la escribe, el parser la separará en tres palabras, antes
siquiera de buscarla en el diccionario. La rutina
SiguientePalabra() nunca podrá retornar el valor 'in,out'. En
realidad esto puede ser útil (véase la sección sobre criaturas vivas y
conversación).
|
![]() |
También puede ser útil comprobar si una palabra es en realidad un
número. La rutina de librería IntentarNumero(num_pal) mira la
palabra que está en la posición num_pal dentro de la secuencia que
el jugador ha escrito, intentando comprenderla como si fuera un
número. Es capaz de reconocer números escritos con dígitos, o
escritos en español (desde "uno" hasta "veinte"). Retorna el
valor del número reconocido, y si no es capaz de interpretar esa
palabra como un número, retornará -1000. Números mayores de 10000 se
recortan a 10000.
|
![]() ![]() |
En ocasiones no queda más remedio que examinar lo que el jugador ha
escrito letra a letra (es decir, examinar la cadena de texto
originalmente escrita por el jugador, en lugar
de la secuencia de palabras que el parser ha separado). Por ejemplo,
si queremos leer un número de teléfono de 20 cifras. La rutina
DireccionDePalabra(num_pal) devuelve un array de tipo byte, que
contiene las letras que componen la palabra en posición num_pal. La
rutina LongitudDePalabra(num_pal) devuelve cuántas letras
tiene. Observa que, al no tratarse de palabras de diccionario sino de
texto real, puede tener más de 9 letras de longitud.
Por tanto, en el ejemplo del hombre pobre de antes, el siguiente código:
imprimiría:
puesto que la palabra 4 es "come". La variable |
Un objeto puede intervenir en cómo se está interpretando su nombre,
proporcionando una propiedad llamada parse_nombre (que ha de ser una
rutina). Lo que se espera de ella es que, comenzando en la posición
indicada por np, intente avanzar el máximo de palabras posible
dentro de la secuencia escrita por el jugador, leyendolas de una en
una con SiguientePalabra(), mientras que todas estas palabras
seguidas se entiendan como referidas al objeto (o lo que es lo mismo,
hasta que encuentre una que no sea aplicable al objeto). Debe retornar
uno de estos valores:
k palabras seguidas que parecen referirse al
objeto.
Una vez que parse_nombre ha retornado, el valor de np puede haber
sido modificado, pero esto no importa a la librería, ya que mantiene
una copia del valor que tenía antes.
El siguiente ejemplo:
Object -> cosa "cosa rara"
with parse_nombre [ i;
while (SiguientePalabra()=='cosa' or 'rara') i++;
return i;
],
has femenino;
|
es prácticamente lo mismo que haber escrito:
Object -> cosa "cosa rara" with nombre 'cosa' 'rara', has femenino; |
por lo que no es demasiado útil. Pero volviendo al tomate
verde que cambia de color, podemos programarlo ahora así
(suponiendo que su atributo general es usado para indicar que ha
madurado):
parse_nombre [ i j;
if (self has general) j='rojo'; else j='verde';
while (SiguientePalabra()=='tomate' or 'frito' or j) i++;
return i;
],
|
por lo que si el jugador pone TOMATE ROJO, este tomate sólo
se dará por aludido si su atributo general está activado. Y si
pone TOMATE VERDE, sólo se dará por aludido si general está
desactivado.
EJERCICIO 56 |
El tomate anterior también se dará por aludido si el jugador pone
ROJO FRITO TOMATE, o FRITO ROJO, por ejemplo (y general está
activo). Escribelo de forma que sólo entienda el orden TOMATE
<color> FRITO, siendo obligatorio TOMATE y las otras dos
opcionales (debe entender TOMATE, TOMATE ROJO, TOMATE FRITO y TOMATE
ROJO FRITO, o sus equivalentes con VERDE)(Solución) |
EJERCICIO 57 |
Crea una cantante llamada Princess que, al ser besada se transforma en
"/.%./ (la artista antes conocida como Princess)". NOTA: date
cuenta que su nuevo nombre /.%./ es "inescribible". Debes
acceder al buffer de letras para parsearlo.(Solución) |
EJERCICIO 58 |
Construye una máquina expendedora de refrescos capaz de servir cola,
café o té, usando sólo un objeto para los tres botones, y otro para la
posible bebida. (Solución) |
![]() |
La propiedad parse_nombre también se ocupa de detectar
plurales. Más información en la sección sobre nombres en
plural.
|
Supón que un objeto no tiene la rutina parse_nombre, o que tiene una
pero ésta ha retornado -1. Lo que hace el parser entonces es mirar la
propiedad nombre. Admite cualquier secuencia de estas palabras,
cuantas más mejor. Así que admitirá "tomate verde frito", y también
"tomate verde" y "tomate frito". Por otro lado, también admitirá
"frito verde" y "verde verde tomate verde frito verde". Este
método es rápido y bueno para comprender una gran variedad de entradas
razonables, pero no es muy buen para deshechar las entradas absurdas.
Sin embargo, puedes modificar esto si escribes una rutina
InterpretarNombre. La rutina InterpretarNombre es llamada por la
librería cuando llega a un punto en el que espera un nombre de un
objeto, y está tratando de decidir cuántas palabras seguidas de las
que vienen a continuación podrían ser el nombre de un objeto. La
librería va probando con todos los objetos al alcance del jugador, y
para cada uno (que no tenga la propiedad parse_nombre) llama a
InterpretarNombre pasándole como parámetro ese objeto.
InterpretarNombre debe hacer lo mismo que parse_nombre, es decir,
retornar -1, 0 o el número de palabras encajadas.
![]() |
En realidad, la librería InformATE ya suministra una rutina
InterpretarNombre para modificar el mecanismo estándar de parsing
que se acaba de describir.
La rutina Básicamente, el mecanismo de parsing que usa esta función consiste en repetir el siguiente bucle:
Aunque pueda parecer confuso, el mecanismo sirve para ignorar la
palabra "de", y los artículos, siempre que estos aparezcan antes de
alguna palabra referida al objeto. Así, si el objeto tiene en La preposición "DEL" es convertida en "DE" antes incluso de que entre el parser, por lo que también es ignorada por este mecanismo. Por otro lado, el mecanismo antes descrito acepta como buena una frase que sólo contenga adjetivos, si bien toma nota de este hecho, por si acaso otros objetos cercanos también "se dan por aludidos". Por ejemplo, imagina que tienes un objeto llamado "madera" (un trozo de madera) y otro llamado "caja de madera". Si en la misma localidad sólo se halla la caja de madera, puede parecer razonable admitir "MIRA MADERA" como una forma de referirse a la caja. Sin embargo, si están juntos la caja y el madero, parece que "MIRA MADERA" debería referirse al madero y no a la caja. La forma en que InformATE resuelve esta ambigüedad consiste en admitir
"provisionalmente" denominaciones que sólo usen el adjetivo, pero en
caso de conflicto, dar prioridad a los objetos que han sido llamados
por su nombre, en lugar de su adjetivo. Así, si el madero tiene
'madera' en su propiedad
|
Por ejemplo, la siguiente rutina haría lo mismo que el parser inglés:
[ InterpretarNombre objeto n; while (PalabraEnPropiedad(SiguientePalabra(), objeto, nombre)) n++; return n; ]; |
siendo PalabraEnPropiedad una rutina de la librería que
sirve para comprobar si una palabra dada está entre las listadas en
una propiedad dada. Esta rutina recibe como primer parámetro la
palabra que hay que buscar, como segundo parámetro el objeto en el
que hay que buscarla, y como tercer parámetro la propiedad dentro de
ese objeto. En el ejemplo anterior, busca en la propiedad nombre.
![]() |
Por si tienes curiosidad, he aquí cómo está programada la rutina
PalabraEnPropiedad():
|
![]() |
|
![]() |
|
![]() |
|
![]() ![]() |
|
![]() ![]() |
|
![]() | ![]() | ![]() | Cómo se interpretan los nombres |