jueves, 16 de octubre de 2025

Html y Prolog

 Aquí tienes un ejemplo realista y práctico: un pequeño servidor web de recomendaciones de películas que utiliza una base de conocimientos en Prolog.

Imagina que tienes una base de datos de películas y quieres que los usuarios puedan encontrar una película introduciendo un género y una calificación mínima. Prolog es perfecto para este tipo de consultas lógicas. library(http) nos permite poner esto en la web.


El Escenario Realista

Crearemos un servidor web local que:

  1. Define un conocimiento base sobre películas (título, género, calificación).

  2. Inicia un servidor web en un puerto específico (ej. 8000).

  3. Escucha peticiones en una ruta, por ejemplo /recomendacion.

  4. Recibe parámetros de la URL, como genero y calificacion_minima.

  5. Consulta la base de conocimientos de Prolog para encontrar películas que coincidan.

  6. Devuelve una página HTML dinámica con los resultados al usuario.


El Código Completo

Copia y pega este código en un archivo llamado servidor_peliculas.pl.

Prolog
% Cargar las bibliotecas necesarias para el servidor HTTP y el manejo de HTML
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/http_parameters)).
:- use_module(library(http/html_write)).

% --- 1. Base de Conocimientos ---
% Hechos: pelicula(Título, Género, Calificación)
pelicula('The Matrix', scifi, 8.7).
pelicula('Inception', scifi, 8.8).
pelicula('The Godfather', crimen, 9.2).
pelicula('Pulp Fiction', crimen, 8.9).
pelicula('Forrest Gump', drama, 8.8).
pelicula('The Dark Knight', accion, 9.0).
pelicula('Interstellar', scifi, 8.6).
pelicula('Parasite', drama, 8.5).

% --- 2. Lógica de Búsqueda ---
% Regla: encontrar_peliculas(Genero, CalificacionMin, Peliculas)
% Encuentra todas las películas que cumplen con el género y la calificación mínima.
encontrar_peliculas(Genero, CalificacionMin, Peliculas) :-
    findall(
        pelicula(Titulo, Genero, Calificacion),
        (pelicula(Titulo, Genero, Calificacion), Calificacion >= CalificacionMin),
        Peliculas
    ).

% --- 3. Manejador de la Petición HTTP ---
% Este predicado se ejecuta cuando alguien visita /recomendacion
manejador_recomendacion(Request) :-
    % Obtener los parámetros de la URL (ej. ?genero=scifi&calificacion=8.7)
    http_parameters(Request,
                    [ genero(Genero, [default('scifi')]),
                      calificacion(CalificacionStr, [default('8.0')])
                    ]),
    % Convertir la calificación de texto a número
    atom_number(CalificacionStr, CalificacionMin),

    % Ejecutar la lógica de búsqueda
    encontrar_peliculas(Genero, CalificacionMin, Resultados),

    % Generar y enviar la respuesta HTML
    reply_html_page(
        title('Recomendador de Películas'),
        [ h1('Recomendador de Películas en Prolog'),
          h2('Resultados de tu búsqueda:'),
          p(['Buscando películas de género: ', b(Genero), ' con calificación mínima de: ', b(CalificacionMin)]),
          \generar_tabla_resultados(Resultados) % Llama a un predicado para crear la tabla
        ]).

% Predicado auxiliar para generar la tabla HTML con los resultados
generar_tabla_resultados([]) --> !, % Si no hay resultados
    html(p(b('No se encontraron películas con esos criterios.'))).
generar_tabla_resultados(Peliculas) --> % Si hay resultados
    html(table([border(1)],
               [ tr([th('Título'), th('Género'), th('Calificación')])
               | \filas_peliculas(Peliculas) % Generar las filas de la tabla
               ])).

% Genera una fila <tr> por cada película en la lista
filas_peliculas([]) --> [].
filas_peliculas([pelicula(Titulo, Genero, Calificacion)|Resto]) -->
    html(tr([td(Titulo), td(Genero), td(Calificacion)])),
    filas_peliculas(Resto).

% --- 4. Configuración y Arranque del Servidor ---
% Definir las rutas (handlers)
:- http_handler('/recomendacion', manejador_recomendacion, []).

% Predicado para iniciar el servidor
servidor(Puerto) :-
    http_server(http_dispatch, [port(Puerto)]).

% Para iniciar, ejecuta: ?- servidor(8000).

¿Cómo Funciona?

Paso 1: Ejecutar el Servidor

  1. Abre SWI-Prolog.

  2. Carga el archivo: ?- [servidor_peliculas].

  3. Inicia el servidor en el puerto 8000: ?- servidor(8000).

Verás un mensaje en la consola indicando que el servidor está corriendo, algo como: % HTTP server running on port 8000

Paso 2: Usar la Aplicación

Abre tu navegador web y ve a la siguiente URL:

http://localhost:8000/recomendacion?genero=scifi&calificacion=8.7

El servidor Prolog recibirá esta petición, ejecutará la lógica y te devolverá una página HTML con este resultado:

Recomendador de Películas en Prolog

Resultados de tu búsqueda:

Buscando películas de género: scifi con calificación mínima de: 8.7

TítuloGéneroCalificación
The Matrixscifi8.7
Inceptionscifi8.8

Si cambias la URL, por ejemplo a ?genero=crimen&calificacion=9.0, obtendrás resultados diferentes, demostrando que la página es totalmente dinámica y controlada por la lógica de Prolog.


Explicación del Código

  • library(http/...): Son las librerías que nos dan todas las herramientas para crear el servidor, manejar las peticiones (Request) y generar respuestas en HTML.

  • pelicula(...): Son nuestros "hechos" en la base de conocimientos. La base de todo.

  • encontrar_peliculas(...): Esta es la lógica principal. Utiliza findall/3 para recopilar en una lista todas las soluciones que cumplen con las condiciones, actuando como un motor de búsqueda.

  • manejador_recomendacion(Request): Es el "controlador". Se activa cuando se visita /recomendacion.

    • http_parameters/2: Extrae los valores de la URL de forma segura (con valores por defecto si no se proporcionan).

    • reply_html_page/2: Es una forma muy cómoda de construir una página HTML. El segundo argumento es una lista de elementos HTML que se renderizan automáticamente.

  • servidor(Puerto): Es el predicado que inicia todo el proceso, creando un servidor que escucha peticiones y las redirige al manejador correcto (http_dispatch).

Este ejemplo muestra perfectamente la separación de responsabilidades: la base de conocimientos (hechos), el motor de inferencia (reglas) y la capa de presentación (el servidor web) trabajan juntos, cada uno haciendo lo que mejor sabe hacer.

Xpce, de Prolog

XPCE es una biblioteca de herramientas (toolkit) para crear Interfaces Gráficas de Usuario (GUI) directamente desde SWI-Prolog. Es un sistema de programación orientado a objetos que está profundamente integrado con el motor lógico de Prolog.

Esto significa que con XPCE puedes construir aplicaciones de escritorio completas, con ventanas, botones, menús, lienzos para dibujar y otros elementos interactivos, todo controlado por la lógica de tu programa en Prolog.

¿Qué se puede hacer con XPCE?

Aquí tienes un desglose de sus capacidades y usos principales, desde lo más simple a lo más complejo:

1. Crear Interfaces Gráficas para tus Programas

En lugar de interactuar con tu programa Prolog solo a través de la consola o terminal, puedes crear una ventana amigable para el usuario. Esto incluye:

  • Ventanas y Diálogos: Crear ventanas principales, cuadros de diálogo para pedir datos (prompt), mostrar mensajes (alert) o confirmaciones.

  • Controles y Widgets: Añadir elementos interactivos como:

    • Botones (button) que ejecutan predicados de Prolog al ser pulsados.

    • Campos de texto (text_item) para que el usuario ingrese datos.

    • Menús (menu_bar, popup_menu) para organizar acciones.

    • Listas (list_browser) para mostrar conjuntos de datos.

    • Sliders, checkboxes, radio buttons, etc.

Ejemplo de uso: Un sistema experto sobre diagnóstico de enfermedades donde el usuario hace clic en síntomas en lugar de escribirlos.

2. Visualización de Datos y Conocimiento

Prolog es excelente para representar estructuras de datos complejas como árboles, grafos y redes. XPCE es ideal para visualizar estas estructuras.

  • Dibujo 2D: Puedes crear un lienzo (picture) y dibujar figuras geométricas (líneas, rectángulos, círculos, polígonos).

  • Grafos y Árboles: Puedes programar la visualización de un árbol de búsqueda, un grafo de dependencias o un árbol genealógico, donde cada nodo puede ser un objeto interactivo.

Ejemplo de uso: El propio depurador gráfico de SWI-Prolog (gtrace.) está hecho con XPCE. Te muestra el árbol de llamadas y el flujo de ejecución de tu programa de forma visual, lo cual es increíblemente útil.

3. Desarrollo de Herramientas y Entornos Interactivos

Debido a su profunda integración, XPCE es perfecto para crear herramientas de desarrollo o software educativo.

  • Editores de código: El editor de código integrado en SWI-Prolog, PceEmacs, está construido enteramente con XPCE.

  • Software educativo: Crear programas para enseñar lógica, matemáticas o cualquier materia donde la interacción y la visualización sean clave. Por ejemplo, un programa para resolver y visualizar puzzles lógicos como el Sudoku o las Torres de Hanoi.

  • Prototipado rápido: Si tienes una idea para una aplicación basada en lógica, puedes usar XPCE para crear rápidamente un prototipo funcional con una interfaz gráfica sin tener que salir del entorno de Prolog.

4. Programación Orientada a Objetos en Prolog

Aunque Prolog es un lenguaje de programación lógica, XPCE introduce un modelo de objetos completo.

  • Clases y Objetos: Puedes definir clases con atributos (variables de instancia) y métodos.

  • Manejo de eventos: El modelo es inherentemente orientado a eventos. Puedes hacer que los objetos reaccionen a acciones del usuario (clics, movimiento del ratón, teclas presionadas).

  • Delegación: Utiliza un mecanismo de envío de mensajes similar a Smalltalk u Objective-C.

Ventajas y Desventajas

  • Ventajas:

    • Integración perfecta: La comunicación entre tu lógica Prolog y la interfaz es directa y natural. No necesitas APIs externas ni complicados puentes entre lenguajes.

    • Viene incluido: No necesitas instalar nada extra, ya es parte de SWI-Prolog.

    • Multiplataforma: Funciona en Windows, macOS y Linux.

    • Potente para herramientas: Es ideal para visualizar estructuras de datos complejas que son el punto fuerte de Prolog.

  • Desventajas:

    • Estética anticuada: La apariencia de los componentes de XPCE no es moderna. Se ve como una aplicación de los años 90 o 2000. No es la mejor opción si buscas una interfaz pulida y moderna.

    • Curva de aprendizaje: Requiere aprender su modelo de programación orientado a objetos, que es un poco diferente al de lenguajes como Java o Python.

    • Alternativas modernas: Hoy en día, es muy común que la lógica de Prolog se ejecute en un servidor y la interfaz de usuario sea una aplicación web (HTML/CSS/JavaScript) que se comunica con el servidor a través de una API. SWI-Prolog tiene excelentes bibliotecas para crear servidores web (library(http)).

Conclusión

XPCE es una herramienta extremadamente poderosa si quieres construir aplicaciones de escritorio, herramientas de visualización o software educativo donde la lógica de Prolog es el núcleo central. Es la forma más rápida y directa de darle una cara gráfica a tu programa Prolog.

Si, por otro lado, tu objetivo es crear una aplicación con una interfaz de usuario moderna y atractiva, probablemente sea mejor opción usar SWI-Prolog como un motor de backend y construir la interfaz con tecnologías web.

jueves, 15 de septiembre de 2016

De lógica

Puntos a tratar:

  1. Todo estaría esencialmente en orden, pero no está demás dar un repaso general y lo siguiente.
  2. No vimos esto de la "diferencia simétrica" entre conjuntos, ya que supuse que estaba basada directamente en las tres operaciones fundamentales: unión, intersección y diferencia de conjuntos. Repasar la definición en la red o en algún libro.
  3. Repasar demostraciones: captar la idea de convencer a alguien de que estamos en lo correcto.
  4. Dar prioridad a los tipos de razonamiento sobre, por ejemplo, inducción matemática.
Mucha suerte.

martes, 5 de julio de 2016

Directivas generales (nuevas)

Nuevo: No espero más aclaraciones, verificaciones y revisiones, así que lo que
sigue es enfocarse, a quienes así convenga, a su 2o. extraordinario, el cual
tomará en consideración el material ya visto junto con algún par de ejercicios
del teorema de Stokes o el de Green. Menciono que tomar dxdy o dydx de una
integral iterada dependera de la simetría del dominio, así que es necesario
tomar en consideración si los barridos son de arriba hacia abajo (forma dydx)
o de derecha a izquierda (forma dxdy).

Para el ejemplo del examen, tendríamos
    2     sqrt(x)
int   int            f(x,y) dy dx
    0      0

y la otra versión (que nadie hizo) es:
    sqrt(2)      2
int            int         f(x,y) dx dy
      0             y*y

Verificando en máxima, tenemos:
(%i6) integrate(integrate(y/(1+x**2),y,0,sqrt(x)),x,0,2);
                                    log(5)
(%o6)                               ------
                                      4


(%i9) integrate(integrate(y/(1+x**2),x,y*y,2),y,0,sqrt(2));
     2
Is y  - 2 positive, negative or zero?

positive;
                                    log(5)
(%o9)                               ------
                                      4

Nuevo (12 de julio de 2016): Ya pueden verificar su calificación en el sistema.
Las aclaraciones sólo serán el día de hoy, de preferencia de 4 a 7.


Nuevo: Tomar en consideración que este examen será colegiado
(departamental), así que se intentará mantener un conjunto básico
de problemas por mi parte, pero otros más pueden venir de otros profesores.
Para este caso, repasar de algunos libros clásicos de cálculo diferencial
e integral.

Nuevo: El examen será el lunes 11 de julio en los salones A9 y A10 de 9 a 11
de la mañana.


El examen está dividido en tres partes:

a) Sería la parte de integración simple:
  1. Integración por partes
  2. Integración por sustitución
  3. Fracciones parciales (por ejemplo: int 1/(x**2+x) dx).
  4. Con integración indefinida.
  5. Manejo de integrales (1/(1+x**2))... sinh x, cosh x, e**x (por cierto, es posible que no se permita el uso de formularios en el examen; recordar algunas fórmulas básicas es, así, indispensable para trabajar sin formularios: escribir unas cuantas veces en un papel las fórmulas trigonométricas varias veces es útil, así como "jugar" con las fórmulas de integración: por ejemplo, la integral de 1/(1+x**2) es... y así en una ronda de memorización).
Advertencias en esta parte: No realizar "operaciones de estudiante novato":
i) 1/(a+b) =/= 1/a+1/b
ii) (a**2)**3 =/= a**5
iii) a**3*a**2 =/= a**6
iv) sqrt(a+b) =/= sqrt(a) +sqrt(b)
v) int (f(x)*g(x)) dx =/= (int f(x) dx) * (int g(x) dx)
x/x =/= 1   (la primera tiene una indefinición en x=0; la igualdad es válida si garantizamos que x=/=0)
Agregando otra falacia:
 vi)  (a+b)**2=a**2+b**2

El caso de las fracciones parciales es también digno de estudiar por sí mismo,
ya que de un denominador complicado se puede pasar a un denominador simple, permitiendo que se aplique alguna fórmula de integración bien conocida (por ejemplo, logaritmos):
Ejemplo sencillo:
1/(x**2-5x+6)=
1/[(x-2)(x-3)]=A/(x-2)+B/(x-3)
y
A/(x-2)+B/(x-3)=(A(x-3)+B(x-2))/[(x-2)*(x-3)]
por lo que
1/[(x-2)(x-3)]=(A(x-3)+B(x-2))/[(x-2)*(x-3)] implica que debemos tener
1=A(x-3)+B(x-2)=Ax+Bx-3A-2B= x(A+B)-3A-2B
Si los polinomios 1 y  x(A+B)-3A-2B deben ser iguales, tendrían que
ser iguales por cada ocasión en que ocurre o no la variable x; así,
1=-3A-2B (*)
A+B=0
De aquí, B=-A, y sustituyendo en (*)
 1=-3A+2A=-A, de donde A=-1, y por lo tanto B=1.
De lo anterior, debería ser cierto que
1/[(x-2)(x-3)]=-1/(x-2)+1/(x-3)
 -1/(x-2)+1/(x-3)=[(3-x)+(x-2)]/[(x-2)(x-3)]=1/[(x-2)(x-3)],
como era requerido.
Lo importante es que integrar esta nueva versión
-1/(x-2)+1/(x-3)

es algo sencillo.



Recordemos además que no está de sobra repasar la factorización de un polinomio, la división de polinomios y la suma y resta. Las prisas o los nervios

a veces nos hacen cometer errores simples en estas operaciones. Es bueno en apartar las operaciones que así se realicen para después complementarlas con los ejercicios que se trabajen.

Cuando el tiempo lo permita, traten siempre de derivar aquella función
que proponen como solución en la integración: se tiene que llegar a la
función original, por el teorema fundamental de cálculo.
Noten también que la definición es:
     b
int   f(x) dx = F(b) - F(a), donde F'(x)=f(x), y no F(a) - F(b)
     a
Alterando el orden se cambia el signo, y puede llevarlos a conclusiones
erróneas.

b) Integrales dobles
  1. Descripción de regiones (arriba a abajo, o izquierda a derecha).
  2. Integrales iteradas; consideraciones sobre las variables.
  3. Integrales dobles sobre regiones.
  4. Teorema de Fubini.
  5. Evaluaciones de integrales sobre regiones con definiciones incluyendo constantes: ejemplo [0,a]x[0,b], con a>0,b>0. 
En el caso de regiones, debe distinguirse bien cómo se caracterizan:
para los casos básicos, es de arriba a abajo (primero dy) o de derecha a
izquierda. No es "conmutativo" este orden!!! (salvo casos simétricos indistinguibles). Por ejemplo, integrando f(x,y)=x*2 en [0,2]x[0,2]
es lo mismo para esta región si evaluamos
     2             2
int          int      f(x,y) dy dx     o si evaluamos
      0            0
     2             2
int          int      f(x,y) dx dy
      0            0

pero no para cuando la región es [0,1]x[0,2].

c) Repasar de Larson, parte de cálculo vectorial.
  1. Gradientes.
  2. Campos vectoriales.
  3. Dominios y codominios.
  4. Rotacional.
  5. Divergencia.
  6. Producto interno.
  7. Producto externo.
  8. Integrales de línea; integrales de longitud de arco.
  9. Repasadita del Teorema de Stokes, de Green. 
En esta parte los errores comúnmente registrados son los de ausencia
de paréntesis, tanto para dar adecuada prioriada al efecto de los operadores
como para señalar con precisión que se trabaja con vectores de cierta dimensión. Además, otro error común en los exámenes previos fue no identificar
los pasos de evaluación finales. A veces es una pena que ya se obtuvieron
en esencia los resultados correctos pero las malas evaluaciones subsiguientes
echan todo a perder.

Un tema en el que muchos fallaron en el examen ordinario fue el de cómo
ensamblar la función potencial f de un campo vectorial conservativo F.

Sea por ejemplo F(x,y)=(x,y)
ya que la derivada parcial de x con respecto a y es 0, y de la derivada
parcial de y con respecto a x es también 0, el campo F es conservativo.
devParcial(f,x)=x implica que f1(x,y)=x**2/2+C1(y)
para una f1(x,y) (incompleta con respecto a f).
devParcial(f,y)=y implica que f2(x,y)=y**2/2+C2(x)

Ahora bien, debemos tener que f1=f2=f!, es solo que f es la que abarca
a f1 y a f2. Pero, por un lado, C1(y) puede ser y**2/2 (que está en la formulación de f2), y por otro C2(x) puede
ser igual a x**2/2, (que está en la formulación de f1)
así que f "completa" es f(x,y)=x**2/2+y**2/2+ C, donde
ahora sí C no tiene compromiso de tener dependencia con respecto a x o
a y: es una constante cualquiera.

C1(y) depende de y, pero al derivarla con respecto a x es 0, así que
C1(y) sí depende y; análogamente para C2(x).

Lo mejor para ver que uno dió la respuesta correcta es verificar que con la
f ensamblada se cumple que grad f = F!!!!
Notemos que para todo lo anterior el antecedente previo es la buena técnica de derivación, y el considerar como bien dominado el concepto de derivada parcial (sin cambiar notaciones bien establecidas).

jueves, 29 de octubre de 2015

Fibonaccis y coálgebras en Haskell


Este es un archivo en modo "Haskell literario"

> x = 2
> y = (\x -> x+2) 2

"Naked expression... ", manejo de este tipo de errores.

Veamos la siguiente álgebra:
<<(+,1)>> = Naturales.
El elemento primigenio, seminal o generador es el 1, y la operación de generación
es el +.

1, 1+1=2, 1+1+1=3, 3+1, ...
1, 2, 3, ...                          
-- #N Cardinalidad de N: El tamaño del conjunto generado. La ocurrencia de un $n$
dentro de la sucesión es su orden (como conjunto ordinal).

Otro ejemplo:
<<({o, y, no},{F,T})>> = Proposiciones lógicas simples con constantes T y F.

F,T,T o F, (T o F) y (no F),...
Cfr. con Prolog.

?- false.
false.

?- true,false.
false.

?- true,true.
true.

?- \+ (true).
false.

?- false < true.
ERROR: </2: Arithmetic: `false/0' is not a function -- ya que < es definido para aritmética
?- false @< true.
true.  --ya que @< compara términos

-----------------------------------------------------------------------------

Para coálgebras:

[1],  %Primer elemento
[1,1], % segundo elemento
[1,1,1]...

en el paso al límite en el crecimiento de esta sucesión tenemos un
elemento "infinito": sería el "último" de una unión de elementos previos:

A1={[1]}
A2= A1 U {[1,1]}
A3= A2 U {[1,1,1]}
....
An = A(n-1) U {[1...replicado n veces]} (U representa la unión de conjuntos)
 A1 C A2 C A3 C A4 C A5 C ... ----> infinito (C representa la contención de conjuntos)

En el paso al límite (cuando n tiende a infinito), tenemos un conjunto infinito, cuyo
´ultimo elemento se dice que ésta en la coálgebra generada por la sucesión An.

Para el ejemplo tratado, se especifica en Haskell así:

> o = 1:o

En el modelo de computación de Haskell que es por medio de evaluación perezosa
un elemento solo se crea si se requiere en un paso de cómputo.

Así se crean los números naturales  a partir de un número n:

> nat n = n: nat(n+1)

Y así se crean todos los números naturales

> l = nat 1

Tomando l, tenemos
[1,2,3,4,5,6...] (infinitamente.

Consideremos ahora la función de Fibonacci y observemos que
si desplazamos la sucesión de Fibonacci con respecto a sí misma y
sumamos columna a columna tenemos otra vez la función de Fibonacci:
1,1,2,3,5,8...
   1,1,2,3,5,8...
____________
1 2 3 5 8 13...
Esto diría que la sucesión  de Fibonacci es más fácil de crear subcreando
dos sucesiones de Fibonacci que creando una sola! Paradójico,  no?


> fibs0 = [1,1,2,3]
> fibs1 = 1:1:[]
> fibs2 = 1:1:[x+y| x <- fibs0, y <- tail fibs0]

x valor 1 y valor 1
x valor 1 y valor 1

Tarea: Generar los números primos por medio de un flujo (comento el cofinal
de una coálgebra).


Si ahora tenemos:
fibs
obtenemos
[1,1,2,3,4,2,3,4,3,4,5,4,5,6]
que no es lo que se desea.

*Main> [x+y | x<-[1,2,3,4],y<-[5,6,7]]
[6,7,8,7,8,9,8,9,10,9,10,11]
*Main> [(x,y) | x<-[1,2,3,4],y<-[5,6,7]]
[(1,5),(1,6),(1,7),(2,5),(2,6),(2,7),(3,5),(3,6),(3,7),(4,5),(4,6),(4,7)]

Hagamos justa (fair) la selección de elementos (1 elemento por lista en cada
ocasión). Utilizamos para ello la función zip:

*Main> zip [1,2,3,4] [5,6,7]
[(1,5),(2,6),(3,7)]

*Main> [(x,y) | (x,y)<-zip [1,2,3,4] [5,6,7]]
[(1,5),(2,6),(3,7)]

*Main> [x+y | (x,y)<-zip [1,2,3,4] [5,6,7]]
[6,8,10]

Finalmente, esta es la función requerida:

> fibs = 1:1:[x+y | (x,y) <- zip fibs (tail fibs)]

He aquí algunas ejecuciones:

*Main> take 5 fibs
[1,1,2,3,5]
*Main> take 8 fibs
[1,1,2,3,5,8,13,21]
*Main> take 12 fibs
[1,1,2,3,5,8,13,21,34,55,89,144]
*Main> take 15 fibs
[1,1,2,3,5,8,13,21,34,55,89,144,233,377,610]
*Main> take 20 fibs
[1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765]
*Main> fibs!!100
573147844013817084101
*Main> fibs!!200
453973694165307953197296969697410619233826

etc.

miércoles, 28 de octubre de 2015

Haskell es especial



*.lhs: Hacen a Haskell intensivamente comentado.

Haskell: Su fundamento es el Cálculo Lambda (Alonzo Church, 1930s).
Haskell: Bloque de trabajo son las funciones (brazo derecho).
Haskell: Los tipos son su brazo izquierdo de trabajo: Char, Int, Float, Double...
              tipos curriados o compuestos vía productos cartesianos.
Haskell: Mónadas: La manera de Haskell de modelar el mundo exterior
               disciplinadamente.
Haskell: Funciones de mayor orden y funciones curriadas.
Haskell: Estrategia genérica de evaluación perezosa (lazy evaluation), lo que
         origina coálgebras: Las coálgebras son estructuras duales a las álgebras.
         En una álgebra queremos la intersección de inclusión; en una coálgebra
         queremos la unión de la inclusión. Las coálgebras permiten justificar la
         introducción y utilizamiento de algo conocido como objetos computacionales
         infinitos.


Ejemplos:
1) Cálculo lambda:

> (\x -> x+1) 2
> map (\x -> (4:)) [[1],[2],[3]]

>  map (\x -> x*x) [1,2,3]

> let s = sin . cos in s 2
> let f = tan . cos . sin in f 10

(Haciendo eco de las palabras del Premio Turing John Backus, de que las
variables son inútiles en la programación.)
El operador punto es composición de funciones.

2) Bloques (sin variables)

> longitud  = sum . unos
> unos = map (\x -> 1)


3) Tipos:

> lonCad :: String -> Int
> lonCad = length

Aquí se enfatiza que lonCad calcula la longitud de cadenas.

4) Visto como el programa Hello.hs.

> filter (\x -> x> 10) [1..100]

A las funciones que toman como argumento a otras funciones se les llama
funciones de mayor orden.

> productoInterno:: ((Float,Float),(Float,Float)) -> Float
> productoInterno ((x1,y1),(x2,y2)) = x1*x2+y1*y2




> productoInterno':: (Float,Float) -> (Float,Float) -> Float
> productoInterno' (x1,y1) (x2,y2) = x1*x2+y1*y2

> productoInterno'':: Float ->Float -> Float -> Float -> Float
> productoInterno''  x1 y1 x2 y2 = x1*x2+y1*y2

> mul2 :: (Float, Float) -> Float
> mul2 (x,y) = x*y+2

Versión curriada (Haskell Curry)

> mul2' :: Float -> Float -> Float
> mul2' x y = x*y+2

map (mul2 2) [1,2,3,4,5]

map (\x -> (mul2 (2,x))) [1,2,3,4,5]
La diferencia es que mul2 trabaja en el dominio (Float,Float) y
y mul2' trabaja en el dominio Funciones: Float -> Float

Esta es la versión (infame) de un programa para crear los
números de Fibonacci.
> fib 1 = 1
> fib 2 = 1
> fib n | n> 2 = fib             (n-1) + fib                 (n-2)
En una próxima ocasión generaremos su versión de flujo.


Mientras, la siguiente función genera un flujo infinito de filas del triángulo de Pascal:

> pascal = p
>   where
> p = [1] : [next a | a <- p]
> next x =  addRows (0:x) x
>                  where
>            addRows (a:x) (b:y) =  (a + b):(addRows x y)
>            addRows [a]   []    =  [a]
>            addRows []    []    =  []

Como ejemplos de ejecución tenemos:

Main> take 5 pascal
[[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
*Main> take 15 pascal
[[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1],[1,5,10,10,5,1],[1,6,15,20,15,6,1],[1,7,21,35,35,21,7,1],[1,8,28,56,70,56,28,8,1],[1,9,36,84,126,126,84,36,9,1],[1,10,45,120,210,252,210,120,45,10,1],[1,11,55,165,330,462,462,330,165,55,11,1],[1,12,66,220,495,792,924,792,495,220,66,12,1],[1,13,78,286,715,1287,1716,1716,1287,715,286,78,13,1],[1,14,91,364,1001,2002,3003,3432,3003,2002,1001,364,91,14,1]]
*Main> pascal!!4
[1,4,6,4,1]
*Main> (pascal!!4)!!3
4