Definir abuelo sin escribir cada caso
Una regla de Prolog define una relación nueva, como abuelo o hermano, a partir de hechos que ya tienes. Con ella, Prolog contesta preguntas que nadie escribió como hecho, y una regla puede incluso usarse a sí misma.
Así se calcula quién es antepasado de quién en un árbol familiar con una sola regla, sea cual sea su tamaño.
Al final de esta lección
Nota
Puedo escribir una regla que usa otros hechos, incluida una recursiva.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Regla | un hecho que es cierto si otros lo son | cheap(I) :- price(I, P), P < 3. |
| Cabeza | lo que afirma la regla, a la izquierda de :- | |
| Cuerpo | lo que debe cumplirse, a la derecha de :- | |
| Recursión | una regla que se usa a sí misma | |
| Caso base | la parte que no se usa a sí misma y que para la recursión |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Una regla es un hecho con condiciones
Una regla tiene dos mitades unidas por :-, que se lee "si". A la izquierda está la cabeza: lo que afirmamos. A la derecha está el cuerpo: lo que debe cumplirse.
En el cuerpo, una coma significa "y". cheap(Item) :- price(Item, P), P < 3. dice: un artículo es barato si tiene un precio P y P es menor que 3.
Una regla sobre precios
price(bread, 1).
price(milk, 2).
price(cheese, 7).
% Un artículo es barato si su precio es menor que 3.
cheap(Item) :- price(Item, P), P < 3.
% Preguntar cheap(X) da bread y milk.
Lee las reglas del laboratorio
docker exec prolog cat family.plSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una regla se pregunta como un hecho
Cuando preguntas por grandparent(tom, X), Prolog busca primero parent(tom, Y) y, con cada Y, busca parent(Y, X). Es el mismo trabajo que dos bucles uno dentro de otro.
Los Runs usan los hechos de la lección anterior. Recuerda que todo lo que Prolog escribe sale en inglés.
Los nietos de tom
docker exec prolog swipl -q -g "forall(grandparent(tom, X), (write(X), nl))" -t halt family.plSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Quién es abuelo de eve
docker exec prolog swipl -q -g "forall(grandparent(X, eve), (write(X), nl))" -t halt family.plSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Todas las parejas de hermanos
docker exec prolog swipl -q -g "forall(sibling(X, Y), (write(X-Y), nl))" -t halt family.plSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejemplo: grandparent(tom, X) paso a paso
| Paso | Qué busca Prolog | Resultado |
|---|---|---|
| 1 | parent(tom, Y) | Y vale ann, y después ben |
| 2 | Con Y = ann: parent(ann, X) | X vale cat |
| 3 | Con Y = ben: parent(ben, X) | X vale dan, y después eve |
| 4 | Respuestas | cat, dan y eve |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Una regla recursiva se usa a sí misma
Un antepasado es un padre, o el antepasado de un padre. Esa frase se usa a sí misma, y por eso se escribe con recursión.
Hacen falta dos reglas. La primera, ancestor(X, Y) :- parent(X, Y)., es el caso base: no se llama a sí misma y para la búsqueda. La segunda, ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y)., sube un escalón y vuelve a preguntar.
Jefes y jefes de jefes
boss(rosa, luis).
boss(luis, ines).
boss(ines, jorge).
% Caso base: un jefe directo.
above(X, Y) :- boss(X, Y).
% Caso recursivo: el jefe de alguien que está por encima.
above(X, Y) :- boss(X, Z), above(Z, Y).
Todos los antepasados de tom
docker exec prolog swipl -q -g "forall(ancestor(tom, X), (write(X), nl))" -t halt family.plSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Quiénes son antepasados de eve
docker exec prolog swipl -q -g "forall(ancestor(X, eve), (write(X), nl))" -t halt family.plSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una pregunta que no se cumple
docker exec prolog swipl -q -g "(ancestor(cat, tom) -> write(yes) ; write(no)), nl" -t halt family.plSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un error frecuente
Advertencia
Mucha gente cree que basta con la regla recursiva. Sin caso base, la recursión no tiene dónde parar y Prolog sigue hasta quedarse sin memoria. Escribe siempre primero el caso más simple, y después el que lo usa.
La regla abuelo, con dos bucles
# Una regla de abuelo son dos búsquedas una dentro de otra.
facts = [["tom", "ann"], ["tom", "ben"], ["ann", "cat"], ["ben", "dan"], ["ben", "eve"]]
def children_of(person):
found = []
for pair in facts:
if pair[0] == person:
found.append(pair[1])
return found
person = "tom"
for child in children_of(person):
for grandchild in children_of(child):
print(person, "es abuelo de", grandchild)
# Cambia "tom" por "ben" y vuelve a ejecutar.
Para ir más lejos
Piensa en dos cambios. ¿Qué pasaría si invirtieras el orden del cuerpo y escribieras primero ancestor(Z, Y) y después parent(X, Z)? Prolog podría quedarse dando vueltas.
Explica con tus palabras por qué la regla sibling lleva X \= Y al final, y qué pareja de hermanos aparecería de más sin esa condición.