% Practico Haskell: un programa con entrada/salida y diccionarios
En este práctico vas a hacer un programa que tiene que tomar el camino a un archivo de texto en línea de comando, y que calcule las palabras más usadas en el archivo.
La idea es empaquetar ese programa con cabal
.
Palabras más frecuentes en un texto
Vas a editar el módulo Main.hs
. La idea es hacer un programa que lea un archivo de texto y muestre
las 10 palabras más frecuentes.
Como archivos de texto podés bajar libros desde
proyecto Gutenberg (https://www.gutenberg.org/browse/languages/es) en formato .txt
, por ejemplo:
- París en América, de Laboulaye
- Actas Capitulares en Buenos Aires 21-25 de mayo 1810
- Informe sobre el Estado de las Clases Obreras Argentinas, de Juan Bialet Massé
Se puede dividir el trabajo en las étapas siguientes:
- leer el contenido del archivo como una sola cadena de caracteres
- partir este contenido como lista de palabras
- procesar esta lista de palabras para contar cuántas veces aparece cada palabra
- ordenar las palabras de más frecuente a menos frecuente, y mostrar las más frecuentes
Para implementar el paso 3, vamos a usar la estructura de datos
Map
(diccionario) del módulo Data.Map.Strict
.
La idea es guardar las palabras y su cantidad de ocurrencias
en un diccionario de tipo Map String Int
.
Más precisamente, se implementará lo siguiente:
- Importá los módulos
Data.Map.Strict
yData.List
dándoles respectivamente el prefijoMap
yList
para evitar colisiones de nombres. - Definí una función
main
que lee el contenido del primer archivo dado como argumento en línea de comando, y imprime las palabras del archivo como una lista de Strings. Indicación: buscágetArgs
en Hoogle. -
Definí una función
agregar :: Map.Map String Int -> String -> Map.Map String Int
que agrega una palabra a un diccionario (posiblemente el diccionario ya tiene la palabra!). Ejemplos:agregar Map.empty "Hola" == fromList [("Hola",1)] agregar ( agregar Map.empty "Hola" ) "Chau" == fromList [("Chau",1),("Hola",1)] agregar ( agregar Map.empty "Hola" ) "Hola" == fromList [("Hola",2)]
-
Definí la función
agregarLista :: Map.Map String Int -> [String] -> Map.Map String Int
que agrega repetitivamente las palabras dadas en una lista.Ejemplos:
agregarLista Map.empty ["Hola"] == fromList [("Hola",1)] agregarLista Map.empty ["Hola","Chau"] == fromList [("Chau",1),("Hola",1)] agregarLista Map.empty ["Hola","Hola"] == fromList [("Hola",2)]
- Modificá el
main
para agregar todas las palabras del archivo a un diccionario vacío para construir un diccionario que contenga todas las palabras del archivo con sus respetivas frecuencias. - Definí la función
top10 :: Map.Map String Int -> [(Int, String)]
que devuelva las 10 palabras más frecuentes del diccionariom
. - Imprimí el top 10 de las palabras del archivo en la salida estándar.
Después de terminar cada paso podés mostrar en la salida estándar lo que llegaste a definir.
Mejoras
Para mejorar este programa, podés implementar los siguientes puntos:
- Mejorar la precisión de tu programa:
- ignorar la puntuación
- ignorar diferencias de mayúsculas y minúsculas
- ignorar género y número de los sustantivos y adjetivos, y persona y tiempo/modos de los verbos
- ignorar las palabras “funcionales” del español, que no reflejan el asunto semántico de un texto: artículos, pronombres, preposiciones, etc.
Por ejemplo, no te sirve que tu programa crea que “Hola,” (con la coma pegada) es una palabra; tampoco te sirve que “Hola” y “hola” sean consideradas como dos palabras distintas. Podés también evitar que “alumno”, “alumna”, “alumnos” y “alumnas” sean consideradas como cuatro palabras distintas.
- Mejorar la interfaz:
- evitar los errores provocados por el uso de
getArgs
- evitar los errores provocados por el uso de
- Mejorar la performancia:
- usar
ByteString
donde se pueda
- usar
- Mejorar el código:
- eliminar los warnings (compilar el programa con -Wall).
- eliminar todo uso de funciones parciales como
head
,tail
,init
,last
y(!!)
, usando pattern-matching y definiciones por caso. - definí
agregarLista
usando una función de pliegue.