Laboratorio Análisis Lógico Práctica 1

Documentos relacionados
Laboratorio de Lenguajes de Programación Introducción al lenguaje ML

Tipos de datos algebraicos

Introducción a Haskell. Cecilia Manzino

CAPÍTULO IV: 4.1 Introducción a la. Programación Funcional

Tema 2: Introducción a la programación con Haskell

Tipos algebraicos y abstractos. Algoritmos y Estructuras de Datos I. Tipos algebraicos

Tema 2: Introducción a la programación con Haskell

Tema 2: Introducción a la programación con Haskell

n! = 1 2 n 0! = 1 (n+1)! = (n + 1) n!

Práctica 1. Programación Funcional Introducción a Helium

Tema 2: Introducción a la programación con Haskell

Algoritmos y programas. Algoritmos y Estructuras de Datos I

Laboratorio Análisis Lógico Práctica 2: Tipos de datos en Haskell

Lenguajes de Programación Programación funcional

Introducción a Python. Cecilia Manzino

Tema 3: Características de la programación funcional. Sesión 5: El paradigma funcional (1)

Apunte Laboratorio ALPI - El lenguaje de programación Pascal

Aplicación de la Inducción Matemática Programación Funcional. Julio Ariel Hurtado Alegría Departamento de Sistemas Universidad del Cauca

Programación Funcional

Para entender la recursividad primero tenemos que entender la recursividad

LENGUAJES Y COMPILADORES

Lenguajes funcionales: λ-cálculo

Componentes Básicos. InCo. InCo Componentes Básicos 1 / 28

Programación Funcional Haskell Clase 19

Listas y Recursión. Taller de Álgebra I. Primer Cuatrimestre de 2015

UNIDAD 2 Descripción de un programa

Introducción a la programación. Cecilia Manzino

Guía 2: Listas, recursión e inducción

Programación Declarativa Haskell Informática Sistemas Curso Pepe Gallardo Universidad de Málaga. Tema 8. Listas

Paradigma Funcional Caso de estudio: ML. Lenguajes de Programación 2017

Tema II: Introducción al Lenguaje Funcional

Paradigma Funcional Caso de estudio: ML. Lenguajes de Programación 2018

Paradigmas de lenguajes de programación. Introducción a la programación imperativa. Lenguaje C. Programación imperativa

roducción a la programación con el lenguaje C usando el entorno de trabajo Dev-C. Nociones básicas de programación

Programación Funcional Haskell Clase 21

Fundamentos de la POO 1

Es un lenguaje estructurado, tiene una abundante cantidad de operadores y tipos de datos.

Funciones Segunda parte

Programación Funcional Lisp-DrScheme Primera Parte. Dr. Oldemar Rodríguez Rojas Escuela de Informática Universidad de Nacional

Resolución de Problemas y Algoritmos

Tipos de datos y clases de tipos

Clase 02/10/2013 Tomado y editado de los apuntes de Pedro Sánchez Terraf

abril de 2017 Desarrollo de aplicaciones en Java Tipos de datos primitivos Tipos de datos Elementos de aplicaciones simples

1.1 Define un operador ( ) que devuelva True si y solo si tan solo uno de sus argumentos es True. Por ejemplo:? (3 > 2) (2 > 5)

Pre-Laboratorio 1 MA-33A : Introducción a Matlab

Programación declarativa ( )

Tema: Entorno a C# y Estructuras Secuenciales.

Introducción a los Algoritmos Tipos, Funciones y Patrones

Lenguajes de programación. Algoritmos y Estructuras de Datos I. Lenguajes compilados. Lenguajes compilados

Lenguaje C. República Bolivariana de Venezuela Fundación Misión Sucre Aldea Fray Pedro de Agreda Introducción a la Programación III

Dra. Jessica Andrea Carballido

Clase 2: El Lenguaje Python

Introducción a la programación Introducción al entorno Haskell

Introducción a Haskell. El lenguaje Haskell

Guía Introductoria. 1 Sistemas Expertos e Inteligencia Artificial / Guía I / Ciclo / Ing. Herson Serrano. Introducción. Objetivos Específicos

Guía 2: Funciones, listas, recursión e inducción

Tema 2: Introducción a la programación con Haskell

Tema: Entorno a C# y Estructuras Secuenciales.

Dra. Jessica Andrea Carballido

Principios de Computadoras II

Presentación del Curso Presencial. Programación en Java Nivel Básico

Datos Elementales y formato de un programa en Java

Entendiendo expresiones lambda en C# con Mono

Tipos en Haskell. Cecilia Manzino

TUTORIAL PROLOG. Lenguajes de programación Universidad Nacional de Colombia Santiago Carvajal Castañeda Mario Andrés Moreno Norato

Informática Haskell Matemáticas Curso Pepe Gallardo Universidad de Málaga. Tema 8. Listas

GUÍA BÁSICA DE SCHEME v.4

Un calculadora avanzada... y algo más!

COLEGIO DE ESTUDIOS DE POSGRADO DE LA CIUDAD DE MÉXICO

Fundamentos PHP. El término puntuación nos referimos a la sintaxis usada en PHP para la terminación de una línea de código (;)

Programación Funcional Haskell Clase 22

Tema 3. Patrones y Definiciones de Funciones

Tema 4: Definición de funciones

Concepto de compilador Intérprete Fases de un Compilador Herramientas de construcción de Compiladores

Tema 4: Definición de funciones

Tema 3. Patrones y Definiciones de Funciones

Todo programa en 'C' consta de una o más funciones, una de las cuales se llama main.

Introducción a la programación

$0 Representa al parámetro cero o nombre del programa $1 Representa al parámetro uno $2 Representa al parámetro dos

Introducción al Análisis Sintáctico

14/03/2018. Problemas, Algoritmos y Programas. Problemas, Algoritmos y Programas. Programas y Lenguajes. Programas y Lenguajes. Programas y Lenguajes

Tipos paramétricos y recursivos

Taller de programación shell

Java Avanzado. Guía 1. Java Avanzado Facultad de Ingeniería. Escuela de computación.

Modelos de Desarrollo de Programas Y Programación Concurrente Clase N 3

PROGRAMACIÓN LÓGICA. David Felipe Rico Hernandez Gabriel Esteban Bejarano Delgado

1. Computadores y programación

Algoritmo, Estructuras y Programación I Ing. Marglorie Colina

Unidad III: Introducción a la Programación

Tutorial Haskell David Julián Guzmán Cárdenas Cristian Alexanther Rojas Cárdenas Luis Ernesto Gil Castellanos

Metodologías de Programación II Introducción a OCaml

Tema 8: Funciones de orden superior

Tema 8: Funciones de orden superior

Conceptos de Programación Intérprete PYTHON

Introducción a PL/SQL

Código de ética. Herramientas. Instalar emacs. Entrar en ubuntu. Abrimos el terminal (desde el menú) Editamos la lista de fuentes de instalación

Introducción a c++ Introducción a la programación EIS Informática III

Transcripción:

Laboratorio Análisis Lógico Práctica 1 Pedro Arturo Góngora Luna 1 1 Introducción 1.1 Haskell y la programación funcional En la década de los 30 s, Alonzo Church desarrolló un lenguaje formal llamado cálculo λ con la finalidad de analizar las propiedades del concepto de función en matemáticas. Con la continua investigación de Church, Curry 2, Kleene y otros, se sabe ahora que el cálculo λ es un modelo completo de computación, equivalente a otros modelos más conocidos como las máquinas de Turing. El paradigma de programación funcional tiene su base en el concepto de función, con sólidos fundamentos teóricos en el cálculo λ, la lógica combinatoria y las ecuaciones recursivas. A diferencia de los lenguajes imperativos, que no se deslindan del todo de las características del hardware, los lenguajes funcionales brindan un mayor nivel de abstracción. Por ejemplo, una oración como x = 5, significa que definimos a x como 5, mas no que asignamos el valor 5 a alguna celda de memoria de la computadora. Haskell es un lenguaje de programación perteneciente a este paradigma. La primera versión apareció al rededor de los 90 s. Desde entonces, el desarrollo ha sido continuo y ahora tenemos un estándar, Haskell 98. Ya el ejemplo de un párrafo anterior nos muestra una de las mayores diferencias (y de mayor confusión para los novatos) con los lenguajes imperativos, en Haskell no existe el concepto de asignación. La ausencia de la asignación en un lenguaje, implica que no tenemos a la mano estructuras como for ó while que aparecen en la mayoría de los lenguajes populares. Lo anterior puede parecer difícil, sin embargo, en Haskell podemos hacer uso de conceptos de más alto nivel como las definiciones recursivas, tipos de datos inductivos, etc. Aunque existen otros lenguajes funcionales más conocidos, como Lisp y Scheme, Haskell es un lenguaje funcional moderno. Es es un lenguaje funcional puro, es decir, no posee ninguna característica imperativa. Es un lenguaje no estricto y de orden superior. Además, tiene características únicas como las mónadas y tipos de datos algebraicos. En definitiva, Haskell provee de características interesantes para los matemáticos, computólogos y entusiastas de la programación funcional en general. 1.2 Implementaciones Existen varias implementaciones de Haskell, sin embargo, las más populares son Hugs y GHC (Glasgow Haskell Compiler). Hugs es un itérprete escrito en C. Es altamente portable y existen versiones para los sistemas operativos más comunes. GHC es un compilador, está escrito en Haskell y permite generar programas ejecutables binarios bastante eficientes. GHC también puede ser usado en modo interactivo (GHCi) de forma similar a Hugs. Ambos ambientes, y otros más, pueden descargarse del sitio oficial de Haskell http://www.haskell.org. 2 Desarrollo 2.1 Manejando el entorno Para iniciar el entorno de Haskell, ingresamos la instrucción $ hugs 1 pedro.gongora@gmail.com 2 Haskell Curry (1900-1982) es uno de los fundadores de la lógica combinatoria, sobre la cual se basa el cálculo λ 1

en la línea de comandos, o la instrucción $ ghci para la versión interactiva de GHC. Como resultado, despues de unas líneas de presentación del programa, obtenemos un prompt como el siguiente Prelude> _ El significado del prompt es que se ha cargado la librería Prelude, la cual contiene definiciones básicas para nuestros programas. Intentemos ahora algunas pruebás sencillas Prelude> 2+1 3 Prelude> 10+5*2 20 Prelude> max 25 30 30 Prelude> max 25 (30 + 1) 31 Para cargar nuestros programas desde un archivo de texto, podemos usar los siguientes comandos del intérprete: :load <MiPrograma.hs> Compila y carga en el intérprete el código Haskell contenido en el archivo de texto MiPrograma.hs (podemos usar la abreviatura :l). :load Sin argumentos, el intérprete retirará de la memoria los módulos cargados anteriormente, dejando sólo Prelude. :reload Repite la última operación :load (esta instrucción puede abreviarse como :r). Por ejemplo, ejecuta un editor de textos (notepad, emacs, vi, etc.) y captura lo siguiente: -- Módulo: Prueba -- En Haskell los módulos empiezan con una letra mayúscula y los -- comentarios de una linea con -- module Prueba where {- Definimos la función cuad Las funciones y variables comienzan con una letra minúscula También tenemos comentarios de varias lineas -} cuad x = x * x -- esta función calcula el cuadrado identidad x = x -- esta es la función identidad y = 0 -- definimos la variable y como 0 Después de guardar el archivo bajo el nombre Prueba.hs, lo cargamos en el intérprete: Prelude> :l Prueba.hs Compiling Prueba ( Prueba.hs, interpreted ) Ok, modules loaded: Prueba. *Prueba> _ 2

Vemos que el prompt cambia para indicarnos que el módulo prueba ha sido cargado exitosamente. Podemos hacer las pruebas correspondientes: *Prueba> cuad 2 4 *Prueba> cuad (cuad 2) 16 *Prueba> identidad 5 5 *Prueba> identidad "hola" "hola" *Prueba> y 0 *Prueba> y + 1 1 Vamos resaltar algunos puntos del programa anterior: Utilizamos los identificadores cuad, identidad e y. En Haskell un identificador comienza con una letra minúscula, y puede seguirse de cualquier letra, mayúscula o minúscula, dígitos, y los caracteres _ y. Por ejemplo, todos los siguientes son identificadores válidos en Haskell: a, x1, x_1 y x. Utilizamos un identificador para nombrar a las variables y a las funciones. Usamos el símbolo = para definir el valor de una variable o el código de una función. Es importante entender la diferencia entre definición y asignación destructiva. En Haskell no existen las asignaciones destructivas (como en Java o C), pues éstas destruyen el valor anterior y lo cambian por uno nuevo. Por el contrario, una definición es inmutable. Esto quiere decir que cuando nos preguntemos por el valor de un identificador, siempre vamos a obtener el mismo resultado. Que en Haskell sólo existan definiciones, nos garantiza que podemos razonar ecuacionalmente sobre nuestros programas. Cuando escribimos en el intérprete: *Prueba> cuad 2 4 lo que estamos preguntando es: cuál es el resultado de aplicar la función cuad a 2? Para resolver esta pregunta, el intérprete simplemente sustituye cuad por el lado derecho de la definición: x * x, y sustituye cada ocurrencia de x por 2. Esto es, resolvemos la siguiente ecuación: 2.2 Funciones y recursión cuad 2 = (x x) {x:=2} = 2 2 = 4 Podríamos empezar con el ya gastado hola mundo, pero como estamos estudiando un lenguaje funcional, mejor pasemos a ejemplos más interesantes. Consideremos la siguiente definición de la función de factorial { 1 si n = 0 fac(n) = n fac(n 1) si n > 0 Podríamos ingresar entonces lo siguiente en nuestro archivo Prueba.hs 3

fac n = if n==0 then 1 else n * fac (n-1) lo cual funciona perfectamente, pero veamos otras definiciones alternativas: fac n n==0 = 1 n>0 = n * fac (n-1) También ésta otra opción fac 0 = 1 fac (n+1) = (n+1) * fac n Esta última versión muestra la característica de pattern matching de Haskell. Para ejecutar las funciones anteriores, las agregamos a nuestro archivo Prueba.hs y lo volvemos a cargar en el intérprete con la instrucción :r. Algunas pruebas: *Prueba> :r [1 of 1] Compiling Prueba ( Prueba.hs, interpreted ) Ok, modules loaded: Prueba. *Prueba> fac 4 24 *Prueba> fac 4 24 *Prueba> fac 4 24 2.3 Listas Uno de los tipos de datos más usados son las listas. Haskell incorpora a las listas dentro de sus definiciones por defecto. Creamos una lista con la siguiente sintaxis *Prueba> [1,2,3,4,5] [1,2,3,4,5] Otra manera de definir las listas es con el operador : *Prueba> 1:2:3:4:5:[] [1,2,3,4,5] donde [] es la lista vacía (o fin de lista). En realidad, la notación con corchetes ([ ]) es una abreviatura para la notación con :. Por ejemplo (el operador == compara dos expresiones y devuelve True si dan el mismo resultado): *Prueba> [1,2,3] == 1:2:3:[] True El operador : puede razonarse como una función que recibe un elemento x, y una lista xs, y nos construye una nueva lista con x al inicio de la lista xs. Por ejemplo: *Prueba> 1:[2,3,4] [1,2,3,4] 4

Lo único que tenemos que cuidar es el hecho de que las listas son homogeneas, es decir, todos los elementos de una lista deben ser del mismo tipo. Lo siguiente es un error: *Prueba> [1, a ] <interactive>:1:1: No instance for (Num Char) arising from the literal 1 at <interactive>:1:1 Probable fix: add an instance declaration for (Num Char) In the list element: 1 In the definition of it : it = [1, a ] El operador : no será útil para definir funciones sobre listas, sin embargo, Haskell provee de otras alternativas para crear listas. Con el operador.. podemos crear listas por rangos. Por ejemplo: *Prueba> [1..5] [1,2,3,4,5] Otro ejemplo: *Prueba> [ a.. z ] "abcdefghijklmnopqrstuvwxyz" nos muestra el rango de caracteres de la a a la z. El ejemplo anterior también ilustra el hecho de que, en Haskell, las cadenas (o strings) son listas de caracteres. Podemos concatenar dos listas (y por lo tanto cadenas) con el operador ++ *Prueba> [1..5] ++ [6..10] [1,2,3,4,5,6,7,8,9,10] 3 Funciones sobre listas Podemos usar la notación : y la característica de pattern matching de Haskell para definir funciones recursivas sobre listas. Por ejemplo, la función: tamano [] = 0 tamano (x:xs) = 1 + tamano xs nos calcula el tamaño de una lista: *Prueba> tamano [-1..100] 102 Otro ejemplo, la función: sumlist [] = 0 sumlist (x:xs) = x + sumlist xs 5

calcula la suma de los elementos de una lista. Aquí ya podemos observar el patrón. Recordemos que el conjunto de (todas) las listas de elementos de tipo A es un conjunto generado inductiva y libremente. Esto es, el conjunto de las listas de elementos en A es la cerradura inductiva de la lista vacía con la función :. Listas de a A = ({[]}) + Entonces, para definir funciones recursivas sobre las listas tenemos que hacerlo por partes: primero la definimos para el caso base (la lista vacía) y después para las siguientes listas, es decir las que se forman con la función constructora :. Una función interesante es map que aplica una función dada a todos los elementos de una lista. Por ejemplo: *Prueba> map succ [1..10] [2,3,4,5,6,7,8,9,10,11] aplica la función succ (sucesor) a todos los elementos de la lista. Aunque la función map ya viene predefinida en Haskell, no es difícil definir nuestra propia versión map usando el patrón que ya vimos: map f [] = [] map f (x:xs) = f x : (map f xs) Y obtenemos el mismo resultado: *Prueba> map succ [1..10] [2,3,4,5,6,7,8,9,10,11] Una lista se compone de dos partes: una cabeza y una cola. Para cualquier lista de tipo: x 1 : x 2 : : [] x 1 es la cabeza, y la lista x 2 : : [] es la cola. Las funciones head y tail nos regresan la cabeza y la cola de una lista, respectivamente: *Prueba> head [1..10] 1 *Prueba> tail [1..10] [2,3,4,5,6,7,8,9,10] 4 Ejercicios 1. Escribe tu propia versión de las funciones head y tail (puedes nombrarlas head y tail ). 2. Traduce la función concat vista en clase a la sintaxis de Haskell. 3. Verifica (haz un esbozo) que la demostración vista en clase, también aplica para su versión en Haskell: (sumlist l1) + (sumlist l2) = sumlist (concat l1 l2) 6