Tarea:
- Practicar con Miyuki y/o ejercicios de lógico de la página
¿Qué vimos hoy?
- Negación
- Inversibilidad
- Forall
- Functores
- Pattern Matching
- Polimorfismo
Repaso clase pasada
elabora(guille, empanadaDeCarne).
elabora(guille, empanadaDeVerdura).
elabora(vale, papasFritas).
elabora(vale, tiramisu).
elabora(vale, lemonPie).
elabora(ale, hamburguesa).
elabora(lu, sushi).
elabora(mar, flan).
% trabaja(Chef, Restaurante)
trabaja(guille, pinpun).
trabaja(vale, laPececita).
trabaja(vale, laParolacha).
trabaja(lu, sushiRock).
trabaja(lu, nipon).
trabaja(ale, guendis).
trabaja(mar, cantin).
% tieneEstilo(Restaurante, Estilo)
tieneEstilo(pinpun, bodegon).
tieneEstilo(laPececita, bodegon).
tieneEstilo(laParolacha, italiano).
tieneEstilo(sushiRock, oriental).
tieneEstilo(nipon, oriental).
tieneEstilo(cantin, oriental).
tieneEstilo(tacoCaja, mexicano).
tieneEstilo(guendis, comidaRapida).
% esCrack/1: un/a chef es crack si trabaja en al menos 2 restaurantes o cocina lemon pie.
esCrack(Chef):-
trabaja(Chef, Restaurante),
trabaja(Chef, OtroResto),
Restaurante \= OtroResto.
esCrack(Chef):-
elabora(Chef, lemonPie).
% seMereceMichelin/1: un restaurante se merece una estrella michelin cuando tiene un/a chef crack y su estilo es michelinero. El estilo michelinero sucede cuando un restaurante tiene estilo oriental o bodegon. Los comida rapida nunca son michelineros.
esMichelinero(oriental).
esMichelinero(bodegon).
seMereceMichelin(Restaurante):-
trabaja(Chef, Restaurante),
esCrack(Chef),
tieneEstilo(Restaurante, Estilo),
esMichelinero(Estilo).
Not
Queremos saber si un chef es de bajo perfil. Un chef es de bajo perfil si trabaja en algún restaurante, pero no es crack.
chefDeBajoPerfil(Chef) :-
trabaja(Chef, _),
not(esCrack(Chef)).
El predicado not es de orden superior ya que niega la consulta dada. No liga variables por lo que no es inversible (no busca quien hace verdadera o falsa esa consulta, simplemente niega lo que encuentra).
Forall
Hasta ahora, estuvimos trabajando con cuantificadores existenciales, es decir, todas nuestras consultas eran del tipo:
∃x / p(x) => q(x)
Pero, sabemos que hay otro cuantificador, el universal. ¿Y cómo podemos trabajar con este cuantificador? 👀
Vamos a utilizar el predicado de orden superior forall/2:
forall(antecedente, consecuente).
Para que el forall responda verdadero, tiene que ocurrir que para todo antecedente que ocurra, su consecuente ocurre.
Por ejemplo, al definir el predicado chefMichelinero/1 nos responde si todos los restaurantes en los un chef trabajó, se merecen la Michelin.
chefMichelinero(Chef) :-
trabaja(Chef, _),
forall(trabaja(Chef, Restaurante), seMereceMichelin(Restaurante)).
Cuidado, ya que forall no es conmutativo, si por ejemplo hiciésemos:
forall(seMereceMichelin(Restaurante), trabaja(Chef, Restaurante)).
Esto, a diferencia de lo que hicimos en chefMichelinero, significa que para todo restaurante que se merece la Michelin, trabajó ese chef.
Al igual que not, el forall es un predicado de orden superior ya que recibe predicados como argumento. ¿Y por qué ligamos al Chef antes de entrar al forall? Porque, siguiendo con las similitudes con el not, forall no liga variables. Es por este motivo que tenemos que “generar” al Chef antes de entrar al forall pero teniendo la precaución de no ligar el Restaurante, ya que queremos todos los restaurantes en los que trabajó un chef.
Si no ligamos la variable Chef, chefMichelinero no será inversible (no podremos hacer consultas del estilo “¿Quién es un chef michelinero?”) y además no hará lo que queremos, si no que preguntará: “Para todo chef y todo restaurante en los que exista una relación de trabajo, ¿es ese restaurante de estilo michelinero?”.
Functores
Hasta ahora, en lógico siempre hicimos consultas en las cuales nuestros individuos eran simples. Pero además de los individuos simples, también existen los individuos compuestos. 🧐 Los functores son individuos compuestos y tienen:
- Un nombre, o etiqueta, que los identifica.
- Una aridad.
En nuestra base de conocimientos teníamos “platos clásicos” (individuos simples), ahora también hay “platos de autor” (individuos compuestos), que pueden ser:
deconstruccion(PlatoClásico, CantidadDeTexturas)ahumado(Ingrediente, TipoDeMadera)coccionAlVacio(Ingrediente, Horas)espuma(Sabor, Textura)
Por ejemplo:
elabora(guille, deconstruccion(tiramisu, 5)).
elabora(guille, ahumado(berenjena, nogal)).
elabora(mar, coccionAlVacio(papa, 2)).
elabora(valen, coccionAlVacio(calabaza, 4)).
elabora(ale, espuma(parmesano, aireada)).
Ahora queremos saber si un chef elabora un plato gourmet, esto se cumple cuando:
- Es sushi o lemon pie.
- Es plato deconstruido con más de 4 texturas.
- Tiene una cocción al vacío de 2 o más horas o se cocina calabaza al vacío.
- Es un plato ahumado con madera de nogal.
- Es una espuma de parmesano aireada o cualquier espuma gelificada.
elaboraPlatoGourmet(Chef):-
elabora(Chef, sushi).
elaboraPlatoGourmet(Chef):-
elabora(Chef, lemonPie).
elaboraPlatoGourmet(Chef):-
elabora(Chef, deconstruccion(_, Texturas)),
Texturas > 4.
elaboraPlatoGourmet(Chef):-
elabora(Chef, coccionAlVacio(calabaza, _)).
elaboraPlatoGourmet(Chef):-
elabora(Chef, coccionAlVacio(_, Horas)),
Horas >= 2.
elaboraPlatoGourmet(Chef):-
elabora(Chef, ahumado(_, nogal)).
elaboraPlatoGourmet(Chef):-
elabora(Chef, espuma(parmesano, aireada)).
elaboraPlatoGourmet(Chef):-
elabora(Chef, espuma(_, gelificada)).
Si bien los functores se escriben como un predicado, NO son un predicado. Como ven, los estamos usando como parámetro en nuestras consultas.
Ahora, mirando 🔭 un poco a nuestro código anterior, podemos ver lógica repetida y eso no nos gusta mucho. 👎
Veamos cómo podríamos cambiar esto…
elaboraPlatoGourmet(Chef):-
elabora(Chef, Plato),
esGourmet(Plato).
esGourmet(sushi).
esGourmet(lemonPie).
esGourmet(deconstruccion(_, Texturas)) :-
Texturas > 4.
esGourmet(coccionAlVacio(calabaza, _)).
esGourmet(coccionAlVacio(_, Horas)) :-
Horas >= 2.
esGourmet(ahumado(_, nogal)).
esGourmet(espuma(parmesano, aireada)).
esGourmet(espuma(_, gelificada)).
Tenemos un par de cosas interesantes acá. 🤔
Primero, el orden importa, al haber pattern matching debemos ir de lo más particular a lo más general. En el caso de la cocción al vacío, si invertimos el orden actual, cuando haya una calabaza con cocción de 1 hora, el ingrediente quedaría atrapado por la variable anónima y como solo tiene 1 hora de cocción no sería gourmet cuando sí debería serlo por ser una calabaza.
Segundo, si prestamos atención a cómo definimos el predicado esGourmet, podemos ver que estamos deconstruyendo (o abriendo) a los functores que representan el tipo de los platos.
Por ejemplo, en esta claúsula, el tipo va a unificar sólo con aquellos platos que sean de cocción al vacío:
esGourmet(coccionAlVacio(_, Horas)) :-
Horas >= 2.
% además la variable Horas va a unificar
% con la cantidad de horas que va a estar al vacío
Además, cada functor va a “saber” con qué claúsula del predicado quedarse.
Todo esto gracias a que tenemos pattern matching. 🎉
Por último, podemos ver como al predicado esGourmet no le importa para nada que forma puede llegar a tener el functor del tipo del plato.
Al hecho de tratar variables indistintamente de su forma lo llamamos polimorfismo 💗 y va a ser un concepto muy importante y útil desde ahora hasta siempre. 😱