PROLOG (IV): Más que lógica

EXPRESIONES ARITMETICAS

Además de con expresiones lógicas, con PROLOG podemos trabajar con expresiones aritméticas , con números enteros (integer), racionales (rational) y en coma flotante (float) y tiene definidas las constantes pi y e

Veamos un ejemplo.

¿Es 5 el resultado de sumar 2 más 3? Verdadero.
¿Es 5 el resultado de sumar 2 más 4? Falso.

Pero hay muchísimas más como podemos ver en el manual de referencias, veamos unas pocas y así enteremos la nomenclatura que usa:

  • between(+Low, +High, ?Value) : Esta expresión es cierta si el valor de la variable (?Value) está comprendido en el intervalo cerrado (+Low,+Higth), así between(2,4,3)  es true.
  • succ(?Int1, ?Int2) : Esta expresión es cierta si el valor del numero entero de la variable ?int2 es el de ?Int1 incrementado en una unidad. Así succ(1,2) es true y succ(1,A) es A=2.
  • >, <, >=, =>, =/=, =:= o is : son los operadores lógicos correspondientes a menor que, mayor que, mayor o igual, menor o igual, desigual e igual. Por ejemplo 1.0 is sin(pi/2)  es true
  • round(+Expr) : redondea +Expr al entero más proximo. Por ejemplo A is round(1.2) nos devuelve A = 1.

LISTAS

Las listas es una estructura de datos que está implementada en PROLOG. Una lista es un conjunto ordenado de datos, que se escribe entre corchetes y separados por comas por ejemplo [X, Y, Z] . Disponemos de un completo conjunto de funciones para manipular listas en PROLOG.

  • = : permite asignar valores [X, Y, Z] = [juan, pablo, pedro]
  • member: indica si un valor está contenido en una lista member(juan,[juan, pablo, pedro]).
  • max_member: devuelve el elemento que ocupa la posición superior en la lista max_member(A,[juan, pablo, pedro]) devuelve A= pedro
  • min_menber: devuelve el elemento que ocupa la posición superior en la lista min_member(A,[juan, pablo, pedro]) devuelve A= juan
  • length([lista],Longitud) : devuelve la longitud de la lista. Así length([juan, pablo, pedro],Longitud) devuelve Longitud = 3.
  • append: une listas. Un ejemplo append([juan, pablo, pedro],[luis],A) devuelve A = [juan, pablo, pedro, luis].
  • delete (lista, elemento, nueva_lista): borra un elemento de una lista. Veamos un ejemplo delete([juan, pablo, pedro], juan, A) devuelve A = [pablo, pedro].
  • nth0(Index,[lista], elemento_buscado): devuelve el valor del indice del valor buscado en la lista, comenzando por 0. Un ejemplo como nth0(Index,[juan, pablo, pedro], pedro) nos devuelve Index = 2.
  • sort([lista], lista_ordenada): ordena una lista de entrada, devolviendo la lista ordenada sort([juan, pablo, pedro, andres], A) devuelve A = [andres, juan, pablo, pedro].

PROLOG (y fin): Para seguir aprendiendo

Comencemos por recordar que hay dos comandos de ayuda en el propio interprete PROLOG y estos son «help» y «apropos»

Preguntamos sobre el comando consult (carga del programa fuente en la memoria del interprete) con help y apropos

Un texto básico para comenzar a profundizar puede ser el del estándar ISO-PROLOG en él encontrará la razón por la que las variables comienzan por mayúscula o guion bajo, y otras curiosidades que he ido comentando y que un programador experto las considera obvias o evidentes.

Otra utilidad muy práctica es el editor e interprete en web de SWI-PROLOG llamado SWISH que permite desarrollar y programas sin necesidad de instalar ningun programa en nuestro dispositivo (por ejemplo desde la tablet o el móvil)

El mismo programa de PROLOG en SWISH

Existe un manual embebido en el propio SWISH que es muy práctico y además puede consultar un pequeño tutorial del SWISH de la universidad de Málaga y una guia del usuario muy didáctica.

También he encontrado útil el minimanual de Roberto torres de Alba y si prefiere leer el manual de referencia del SWI Prolog en formato libro aquí está el PDF.

Con esto doy por terminada esta introducción a lo que es PROLOG en 2020, un lenguaje que ayuda a pensar de forma lógica.

PROLOG (III): Un primer programa

El ciclo de programación es escribir y probar. Para escribir un programa podemos usar desde un sencillo Bloc de Notas, a un editor como el ZeroBrane Studio (el que uso últimamente y al que me aficioné desarrollando en LUA) a un entorno como Eclipse.

Sencillo programa genearologico en PROLOG

Vamos a analizar este primer programa:Los comentarios se pueden escribir entre «/*» «*/» o tras el signo «%«

Los hechos se escriben como relación(valor1,valor2) y así «José es el padre de Juan» se codifica como «padre(jose, juan).» Las sentencias se terminan en «.» y los valores comienzan por minuscula y el nombre de las variables con mayusculas.

Una regla (Clausula de Horn, cláusulas con al menos un literal positivo) como «la abuela es la madre de la madre o la madre del padre» se codifica como » abuela(A,C) :- madre(A,B), madre(B,C) ; madre(A,B), padre(B,C):«. Siendo «:-» el equivalente del «if» condicional la «,» es un «AND LOGICO» o «Y gramatical» y el «;» el «OR LOGICO» o «O gramatical«.

La forma de aprender a programar es programando, así que imagine cualquier campod e conocimiento y escriba su programa en PROLOG.

Antes de ejecutar nuestro programas tenemos que asegurarnos que el interprete va a poder localizar el fuente, para poderlo cargar e interpretar. Para ello lo primero que debemos conocer es cual es el directorio de trabajo de nuestro interprete, cosa que podemos conocer con el comando «working_directory(CWD, CWD).» o con «pwd»(Les sonará a los que han trabajado con UNIX alguna vez), en el caso que queramos modificar el directorio de trabajo por, por ejemplo «//Users/Juan/Applications/» ejecutariamos el comando de nuevo pero como «working_directory(CWD,»//Users/Juan/Applications/»).» Para conocer el contenido del directorio de trabajo usamos el comando, que en nuestro caso sería «directory_files(«/applications/»,DIR).»

En todos estos comandos el primer campo es el de entrada «+» y el segundo la salida ««.

Si nuestro programa se llama «familia.pl» podemos conocer su path absoluto con el comando «absolute_file_name(«prolog-1.pl»,A).» que en nuestro caso responde con «A = ‘/Users/Juan/Applications/prolog-1.pl’.«

Para cargar en memoria el programa ejecutamos el comando «consult(«/Users/Juan/Applications/familia.pl»).» , «consult(familia)» o su equivalente «[familia].» Si queremos recargar el programa podemos hacer «reconsult(familia)«

Ahora ya podemos comenzar a interpelar a nuestra base de conocimientos

En la primera entrada preguntamos «¿De quién es padre jose?» a lo que nos responde «juan», en la segunda «¿Es jose el padre de juan» a lo que nos responde «true» (cierto), en la tercera «¿jose es el padre de mariajo?» a los que nos responde «false» (falso), en la cuarta «¿Cómo se llaman los nietos de jose?» a lo que nos responde primeramente «damian» tras lo cual da «false» cuando le pedimos más con «;», la quinta es una repetición

De nuevo le invito a jugar con su imaginación a la hora de preguntar a la base de conocimiento

En el primer caso preguntamos por los hijos de jose o los de arturo mediante «;«, y en el segundo por los hijos comunes con «,«

Un listado exhaustivo de todos hechos se consigue con el comando findall, cuyo formato es findall(+Template, :Goal, -Bag) así para encontrar todos los hijos de cualquier padre haremos «findall(X,padre(_,X),Lista)«y nos devolverá «Lista = [juan, mariajo, damian]«

Si encontramos algún problema en la ejecución podemos invocar el modo de traceo mediante «trace» y salir de él con «nodebuger».

Por ultimo debe ser consciente que una misma base de conocimiento puede codificarse de muchas formas, por ejemplo la definición de padre y madre puede escribirse de la siguiente forma

Una posibilidad de las reglas en PROLOG es la recursividad, que permite una regla llamarse a si misma. Con el ejemplo del calculo del Factorial de un número lo veremos con facilidad

La primera regla define que el factorial de 0 es 1. En la segunda regla damos en N el valor del cual queremos calcular el valor de su factorial que nos devuelve en F, en donde Nsea mayor que 0, decrementa el valor de N, invoca a factorial y multiplica el resultado parcial por el valor de N

En entradas sucesivas analizaremos otros aspectos del lenguaje y por último hablaremos de manuales de usuarios (user manual) o cursos que le ayuden a profundizar en el manual de referencia de SWI-PROLOG que es muy extenso y poco didáctico como todos los «references manuals»

PROLOG (II): Los primeros pasos con SWI Prolog

El PROLOG estuvo de moda en los finales de los 80 y principios de los 90, pero ¿Y en la actualidad? ¿Se puede continuar programando en PROLOG?¿Tiene algún sentido hacerlo?.

Pues si, se puede seguir desarrollando en lenguaje PROLOG o ejecutar antiguos desarrollos. Existen al menos dos distribuciones disponibles de PROLOG sobre varios Sistemas Operativos. La primera se llama  SWI PROLOG  (1987) desarrollada por Jan Wielemaker de la Universidad de Amsterdam, se trata de una distribución gratuita y que goza de una licencia de software libre. La otra distribución es Quintus PROLOG orientada a su uso industrial desarrollada en Estados Unidos por Quintus Corp y cuya ultima versión es la 3.5 de 2003 en la actualidad la comercializa una empresa de Suecia. Esto me ha llevado a refrescar el tema, y escribir unas pocas entradas que permitan al curioso e inquieto a explorar este lenguaje, …. hubo vida antes que el Java que era mucho más lógica y comprensible.

Vamos a comenzar dando los primeros pasos para romper el miedo inicial.

Primeramente es necesario descargarse e instalar el interprete de SWI PROLOG (la versión actual en marzo de 2020 es la 8.0.3-1 y está disponible para LINUX, Mac y Windows) que tiene una sintaxis próxima al ISO-PROLOG para nuestro sistema. Una vez realizada la instalació hay que probar que funciona correctamente para ello debemos comprobar que al ejecutarlo nos aparecerá el símbolo o «promp» del intérprete de SWI-PROLOG que es «?-». El interprete ejecuta la sentencia cuando se las sentencias termina con «.« y <ENTER>

Aunque es una obviedad los lenguajes pueden ser interpretados (se convierte las instrucciones de alto nivel en lenguaje maquina u objeto cada vez que se ejecuta) o compilados se realiza esta conversión una única vez y es el objeto lo que se ejecuta cada vez. antaño por la escasez de recursos de ejecución se prefería trabajar con objetos compilados antes que con fuentes interpretables, pero hoy en día esto ha cambiado.

PROLOG termina toda ejecución con un Verdadero (True) o falso (False), a fin de cuentas ejecuta sentencias lógicas.

Si ejecutamos la sentencia «true» PROLOG responde «true» (cierto), y si ejecutamos «false» responde false (falso). Si ejecutamos true, false es false pues , representa el AND lógico y si ejecutamos true; false devuelve true y false pues ; representa el OR lógico (Para otras estructuras de control ver aquí)
Una de las primeras cosas que queremos que haga un ordenador es que nos salude con la tradicional frase de «Hello world» (Hola mundo). Al «Hola Mundo» siempre añadirá PROLOG «True» o «False»

Hemos ejecutado tres comandos (format, print, write) con los que obtenemos que nos responda «Hola mundo» y que tras la ejecución devuelve «true» o «false».

Otros comandos de línea útiles son help (ayuda, por ejemplo help(format) nos informa del comando format que ya hemos usado ), version (información sobre la versión), license (información sobre la licencia o condiciones de uso del interprete).

Como en otros lenguajes las variables comienzan por mayusculas.

Para salirnos del interprete «halt«

Un programa PROLOG lo componen hechos (facts) y reglas (rules), lo ideal es escribirlas mediante un editor y cargarlo en el SWI, pero el interprete nos permite definirlos en linea (on-line). Para definirlos on line se entra con el comando [user] y se sale con CTRL-D

Los hechos se definen mediante :- su posterior ejecución nos devuelve el valor
La regla «La fruta del naranjo es la naranja» se codifica como fruta(naranjo,naranja), cuando preguntamos «¿La fruta del naranjo es la naranja?»fruta(naranjo,naranja), el sistema nos responde «True» y a la pregunta ¿La fruta del naranjo es la manzana ? la respuesta es «false».
También podemos preguntar en la forma ¿Cuál es la fruta del naranjo? fruta(naranjo,Respuesta)
Podemos listar las reglas mediante el comando «listing»
Ahora vamos dar de alta comidas típicas italianas y chinas, como «La pizza es italiana» italiana(pizza) , … también una regla como «A Paolo le gusta la comida italiana» gusta(paolo,A) :- italiana(A). Con todo ya podemos preguntar al sistema ¿Le gusta a Paolo la Pizzza? gusta(paolo,pizza) ¿Le gusta a Paolo los baozi? gusta(paolo,baozi)
Se puede realizar la pregunta ¿Qué platos le gustan a Paolo?  gusta(paolo, Platos), si a la primera respuesta respondemos con . solamente nos da un valor, y si lo hacemos con ; nos devuelve todas las respuestas posibles

Creo que con estos ejemplos se empieza a comprender cómo trabajar PROLOG con los hechos y las reglas. En la próxima entrada vamos a escribir un pequeño programa y lo ejecutaremos