introalg:taller07_6
Diferencias
Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
| introalg:taller07_6 [2007/06/11 21:12] – nicolasw | introalg:taller07_6 [2025/11/15 13:47] (actual) – editor externo 127.0.0.1 | ||
|---|---|---|---|
| Línea 1: | Línea 1: | ||
| ====== Frecuencia de Palabras ====== | ====== Frecuencia de Palabras ====== | ||
| - | Nos proponemos hacer un programa funcional que **cuente la frecuencia de las ocurrencias de palabras**. | + | Nos proponemos hacer un programa funcional que **cuente la frecuencia de las ocurrencias de palabras** |
| + | El tipo de la función es el siguiente: | ||
| < | < | ||
| Línea 7: | Línea 8: | ||
| </ | </ | ||
| - | Main> frecuencia [" | + | Donde '' |
| - | [(" | + | |
| - | + | ||
| - | Donde '' | + | |
| < | < | ||
| Línea 16: | Línea 14: | ||
| </ | </ | ||
| - | Esta función resulta de gran utilidad para **análisis estadístico de textos**, como el que se utiliza para comprimir archivos, dar importancia a las palabras en las búsquedas, detectar palabras significativas, | + | Y esperamos este tipo de comportamiento. |
| + | |||
| + | Main> frecuencia [" | ||
| + | [(" | ||
| + | |||
| + | Esta función resulta de gran utilidad para **análisis estadístico de textos**, como el que se utiliza para comprimir archivos, dar importancia a las palabras en las búsquedas | ||
| + | |||
| ===== División en Palabras ===== | ===== División en Palabras ===== | ||
| - | Para hacer más fácil el uso de esta función tomamos de [[http:// | + | Para hacer más fácil el uso de esta función tomamos de [[http:// |
| Veamos el código de " | Veamos el código de " | ||
| Línea 39: | Línea 44: | ||
| tomaPalabra :: String -> String | tomaPalabra :: String -> String | ||
| tomaPalabra [] = [] | tomaPalabra [] = [] | ||
| - | tomaPalabra (x: | + | tomaPalabra (x: |
| - | | otherwise | + | | otherwise |
| tiraPalabra :: String -> String | tiraPalabra :: String -> String | ||
| tiraPalabra [] = [] | tiraPalabra [] = [] | ||
| - | tiraPalabra (x: | + | tiraPalabra (x: |
| - | | otherwise | + | | otherwise |
| tiraEspacio :: String -> String | tiraEspacio :: String -> String | ||
| tiraEspacio [] = [] | tiraEspacio [] = [] | ||
| - | tiraEspacio (x: | + | tiraEspacio (x: |
| - | | otherwise | + | | otherwise |
| Línea 59: | Línea 62: | ||
| espacio :: [Char] | espacio :: [Char] | ||
| - | espacio = [' | + | espacio = [' |
| </ | </ | ||
| - | |||
| - | |||
| Línea 69: | Línea 70: | ||
| * Definir la función politípica //elemento :: Eq a => a -> [a] -> Bool//, donde //elemento a xs// decide si //a// está en la lista //xs//. Ejemplo: //elemento ' | * Definir la función politípica //elemento :: Eq a => a -> [a] -> Bool//, donde //elemento a xs// decide si //a// está en la lista //xs//. Ejemplo: //elemento ' | ||
| Casos de Test: '' | Casos de Test: '' | ||
| - | * Usando la definición de esta función | + | * Habiendo definido // |
| Casos de Test: | Casos de Test: | ||
| < | < | ||
| Línea 80: | Línea 81: | ||
| ... | ... | ||
| </ | </ | ||
| - | * Las funciones // | + | * Las funciones // |
| * Defina la función // | * Defina la función // | ||
| * Defina la función // | * Defina la función // | ||
| - | * ?Como se llaman // | + | * ¿Cómo |
| - | * Defina // | + | * Defina // |
| - | + | ||
| ===== Lectura del Archivo ===== | ===== Lectura del Archivo ===== | ||
| Línea 114: | Línea 113: | ||
| ((take 100).partePalabras.leeArchivo) " | ((take 100).partePalabras.leeArchivo) " | ||
| [" | [" | ||
| + | |||
| ==== Ejercicios ==== | ==== Ejercicios ==== | ||
| - | * Contar | + | * Contar |
| * Obtener las palabras de "El Gaucho Martín Fierro" | * Obtener las palabras de "El Gaucho Martín Fierro" | ||
| - | * Observar que hay problemas con // | + | * Observar que hay problemas con // |
| Línea 138: | Línea 138: | ||
| agregaOcurrencia :: Palabra -> [(Palabra, | agregaOcurrencia :: Palabra -> [(Palabra, | ||
| </ | </ | ||
| - | que procesa una palabra, la definición de // | + | que procesa una palabra, |
| Línea 145: | Línea 146: | ||
| ==== Ejercicios ==== | ==== Ejercicios ==== | ||
| - | * Definir la función // | + | * Definir la función // |
| Casos de test: '' | Casos de test: '' | ||
| * A partir de // | * A partir de // | ||
| Caso de test: '' | Caso de test: '' | ||
| * Componer // | * Componer // | ||
| + | |||
| ===== Ordenando por frecuencias ===== | ===== Ordenando por frecuencias ===== | ||
| - | El esquema presentado hasta ahora ('' | + | El esquema presentado hasta ahora ('' |
| * Compresión de texto: estas palabras se deben codificar " | * Compresión de texto: estas palabras se deben codificar " | ||
| * Relevancia de palabras para búsquedas: a estas palabras no hay que darles peso. | * Relevancia de palabras para búsquedas: a estas palabras no hay que darles peso. | ||
| - | * Detectar palabras | + | * Detectar palabras |
| La lista que devuelve // | La lista que devuelve // | ||
| Línea 177: | Línea 179: | ||
| ordenaIns _ = [] | ordenaIns _ = [] | ||
| </ | </ | ||
| - | |||
| - | |||
| ==== Ejercicios ==== | ==== Ejercicios ==== | ||
| Línea 187: | Línea 187: | ||
| * Agregar // | * Agregar // | ||
| * Redefinir // | * Redefinir // | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | |||
| + | |||
| + | |||
| ===== Optimización del Cómputo ===== | ===== Optimización del Cómputo ===== | ||
| - | El cálculo de la frecuencia resulta ineficiente, por cada palabra tenemos que recorrer toda la lista de frecuencias '' | + | El cálculo de la frecuencia resulta ineficiente. Por cada palabra tenemos que recorrer toda la lista de frecuencias '' |
| - | De hecho para las aproximadamente 15000 palabras que contiene "El Gaucho Martín Fierro", | + | De hecho para las aproximadamente 15000 palabras que contiene "El Gaucho Martín Fierro", |
| Una posible mejora sería mantener **ordenada por palabras** la lista de frecuencias, | Una posible mejora sería mantener **ordenada por palabras** la lista de frecuencias, | ||
| Línea 202: | Línea 209: | ||
| agregaOcurrenciaOrd palabra [] = [(palabra, | agregaOcurrenciaOrd palabra [] = [(palabra, | ||
| agregaOcurrenciaOrd palabra ((p,n):xs) | p==palabra = (p,n+1) : xs | agregaOcurrenciaOrd palabra ((p,n):xs) | p==palabra = (p,n+1) : xs | ||
| - | | p< | + | | p< |
| | p> | | p> | ||
| Línea 210: | Línea 217: | ||
| </ | </ | ||
| - | Activamos la llave que muestra la //cantidad de reducciones// | + | Activamos la llave que muestra la //cantidad de reducciones// |
| Main> :set +s | Main> :set +s | ||
| Línea 223: | Línea 230: | ||
| (105556 reductions, 232333 cells) | (105556 reductions, 232333 cells) | ||
| - | Ouch! | + | D'oh! |
| Al parecer la idea no es buena, y se pone peor a medida que aumentamos la cantidad de palabras que tomamos en el '' | Al parecer la idea no es buena, y se pone peor a medida que aumentamos la cantidad de palabras que tomamos en el '' | ||
| - | Arbol!!!! | + | Podemos intentar cambiar de manera salvaje la **estructura de datos**, es decir cambiar la //lista de pares// por otra estructura que **mejore el tiempo de búsqueda de una palabra**. |
| + | |||
| + | Una de las estructuras de datos más comunes que mejora de manera dramática el tiempo de búsqueda son los [[http:// | ||
| + | |||
| + | Esta estructura de datos es ligeramente más complicada que la lista, cada nodo tiene: | ||
| + | * Dato | ||
| + | * Subárbol izquierdo | ||
| + | * Subárbol derecho | ||
| + | donde el dato que está en la **raíz** es mayor que todos los del subárbol izquierdo y menor que todos los del subárbol derecho. | ||
| + | |||
| + | Luego con esta forma de estructurar los datos, realizar una búsqueda es sencillo: | ||
| + | * si el dato que buscamos es **menor** que la raíz lo buscamos en el árbol **izquierdo**, | ||
| + | * en cambio si es **mayor** que la raíz, miramos el árbol **derecho**, | ||
| + | * si el dato está en la raíz, listo. | ||
| + | |||
| + | Para definir estos árboles haremos uso de la **definición de tipos de datos** en Haskell '' | ||
| + | |||
| + | < | ||
| + | data ArbolFrec = Nodo (Palabra, | ||
| + | |||
| + | agregaOcurrenciaArbol :: Palabra -> ArbolFrec -> ArbolFrec | ||
| + | agregaOcurrenciaArbol palabra Nulo = Nodo (palabra,1) Nulo Nulo | ||
| + | agregaOcurrenciaArbol palabra (Nodo par@(p,n) izq der) | ||
| + | | p==palabra = Nodo (p,n+1) izq der | ||
| + | | p< | ||
| + | | p> | ||
| + | |||
| + | frecuencia'' | ||
| + | frecuencia'' | ||
| + | </ | ||
| + | |||
| + | Lo probamos para ver el resultado en las 100 primeras palabras y obtenemos: | ||
| + | |||
| + | Main> (frecuencia'' | ||
| + | ERROR - Cannot find " | ||
| + | *** Expression : (frecuencia'' | ||
| + | *** Of type : ArbolFrec | ||
| + | |||
| + | El problema se soluciona desactivando una llave de Hugs | ||
| + | |||
| + | Main> :set -u | ||
| + | Main> (frecuencia'' | ||
| + | ArbolFrec_Nodo (" | ||
| + | |||
| + | ... y esto no se entiende nada! | ||
| + | Entonces definimos una función que " | ||
| + | |||
| + | < | ||
| + | aplanaArbol :: ArbolFrec -> [(Palabra, | ||
| + | aplanaArbol Nulo = [] | ||
| + | aplanaArbol (Nodo par izq der) = par : (aplanaArbol izq ++ aplanaArbol der) | ||
| + | </ | ||
| + | |||
| + | Ahora si veamos como se comporta la versión ABB del problema: | ||
| + | |||
| + | Main> (aplanaArbol.frecuencia'' | ||
| + | [(" | ||
| + | (58403 reductions, 104322 cells) | ||
| + | |||
| + | |||
| + | Woohoo! | ||
| + | |||
| + | La siguiente tabla resume las reducciones en de las 3 versiones y para 3 cantidades de palabras: | ||
| + | |||
| + | ^ algoritmo\palabras ^100 ^1000 ^10000 ^ | ||
| + | | frecuencia' | ||
| + | | frecuencia | ||
| + | | frecuencia'' | ||
| + | |||
| + | Notar que la mejora de // | ||
| + | |||
introalg/taller07_6.1181607178.txt.gz · Última modificación: (editor externo)
