2012-05-06 20 views
5

Soy relativamente nuevo en Haskell así que me disculpo si mi pregunta suena estúpida. He estado tratando de entender cómo funciona la composición de funciones y me he encontrado con un problema que me preguntaba si alguien podría ayudarme. Estoy usando un mapa en una composición de funciones en los dos escenarios siguientes:Haskell: Usando el mapa en la composición de la función

  • map (*2) . filter even [1,2,3,4]
  • map (*2) . zipWith max [1,2] [4,5]

Aunque tanto el filtro y las funciones zipWith devuelven una lista, sólo la primera composición funciona mientras que el segundo composición arroja el siguiente error:

"Couldn't match expected type '[Int] -> [Int]' with actual type '[c0]' 

Cualquier sugerencia sería muy apreciada.

+1

¿Las respuestas a [esta pregunta] (http://stackoverflow.com/questions/2834626/haskell-dot-operator) ayudan? (Especialmente [éste] (http://stackoverflow.com/a/2834661/1256624)) – huon

+1

El primero realmente produce la salida 'No se pudo hacer coincidir el tipo esperado a0 -> [b0] 'con el tipo real [a1] '' –

Respuesta

5

El resultado de zipWith max [1,2] [4,5] es una lista, no una función. El operador (.) Requiere una función como su operando derecho. De ahí el error en su segunda línea. Probablemente lo que quiere es

map (*2) (zipWith max [1,2] [4,5]) 

Su primer ejemplo no se compila en WinHugs (modo Hugs); tiene el mismo error Lo siguiente funcionará

(map (*2) . filter even) [1,2,3,4] 

ya que compone dos funciones y aplica la función resultante a un argumento.

16

Recordar el tipo de (.).

(.) :: (b -> c) -> (a -> b) -> a -> c 

Toma tres argumentos: dos funciones y un valor inicial, y devuelve el resultado de las dos funciones compuestas.

Ahora, la aplicación de una función a sus argumentos se une más estrechamente que el operador (.). Así que su expresión:

map (*2) . filter even [1,2,3,4] 

se analiza como:

(.) (map (*2)) (filter even [1,2,3,4]) 

ahora, el primer argumento, map (*2) es aceptable. Tiene el tipo (b -> c), donde b y c es Num a => [a]. Sin embargo, el segundo argumento es una lista única:

Prelude> :t filter even [1,2,3,4] 
filter even [1,2,3,4] :: Integral a => [a] 

por lo que el tipo de corrector se quejará de que estás pasando un [a] como argumento cuando la función (.) necesita una función.

Y eso es lo que vemos:

Couldn't match expected type `a0 -> [b0]' with actual type `[a1]' 
In the return type of a call of `filter' 
In the second argument of `(.)', namely `filter even [1, 2, 3, 4]' 
In the expression: map (* 2) . filter even [1, 2, 3, 4] 

Entonces ... parentización!

utilizar el operador $ para agregar un paréntesis:

map (*2) . filter even $ [1,2,3,4] 

o utilizar parens explícitas, la eliminación de la composición de dos funciones

map (*2) (filter even [1,2,3,4]) 

o incluso:

(map (*2) . filter even) [1,2,3,4] 
+0

gracias Don. eso fue realmente útil – wajeeh

+1

Si fue útil, debes aceptar la respuesta de Don. – augustss

+1

Sé que OP ha preguntado específicamente sobre la composición de funciones, pero este ejemplo es realmente el más adecuado para remontar '$', _i.e._, 'map (* 2) $ filter even [1,2,3,4]' . – apc

4

Los los siguientes formularios son válidos:

map (* 2) $ filter even [1, 2, 3, 4] 
(map (* 2) . filter even) [1, 2, 3, 4] 
map (* 2) $ zipWith max [1, 2] [4, 5] 
(\xs -> map (* 2) . zipWith max xs) [1, 2] [4, 5] 

pero no los siguientes:

map (* 2) . filter even [1, 2, 3, 4] 
map (* 2) . zipWith max [1, 2] [4, 5] 
(map (* 2) . zipWith max) [1, 2] [4, 5] 

¿Por qué es así? Bueno, tomar por ejemplo

map (* 2) . zipWith max [1, 2] [4, 5] 

es el mismo que

(map (* 2)) . (((zipWith max) [1, 2]) [4, 5]) 

(map (* 2)) ha escriba [Int] -> [Int] (suponiendo el impago por Int), (((zipWith max) [1, 2]) [4, 5]) tiene tipo [Int] y (.) tiene escriba (b -> c) -> (a -> b) -> a -> c o ([Int] -> [Int]) -> ([Int] -> [Int]) -> [Int] -> [Int] en este no caso polimórfico, así que esto está mal escrito. Por otro lado ($) tiene escriba (a -> b) -> a -> b, o ([Int] -> [Int]) -> [Int] -> [Int] en este caso no polimórficos, así que esto:

(map (* 2)) $ (((zipWith max) [1, 2]) [4, 5]) 

es bien mecanografiadas.

2

Debido a la baja prioridad de (.), Haskell analiza

map (*2) . filter even [1,2,3,4] 

como

map (*2) . (filter even [1,2,3,4]) 

es decir componen map (*2) (una función) con el resultado de filter even [1,2,3,4] (una lista), que no tiene sentido y es un error de tipo.

Puede solucionar este problema utilizando @ sugerencias de Theodore, o mediante el uso de ($):

map (*2) . filter even $ [1,2,3,4] 
2

Si comprueba el tipo de mapa que es: (a -> b) -> [a] -> [b]

Por lo tanto, se necesita una función de A en B y luego una lista de a y devuelve una lista de b. ¿Derecha?

Ahora, ya proporciona una función de a en b pasando el parámetro (*2). Entonces, su función de mapa parcialmente aplicada será: [Integer] -> [Integer], lo que significa que recibirá una lista de enteros y devolverá una lista de enteros.

Hasta este punto, podría componer (.) Una función que tiene la misma firma. Si comprueba cuál es el tipo de filter even, verá que es: [Integer] -> [Integer], como tal, un candidato válido para la composición aquí.

Esta composición continuación, no altera la firma final de la función, si se comprueba el tipo de: map (*2) . filter even es [Integer] -> [Integer]

Este no sería el caso de la map (*2) . zipWith max [1,2] [4,5] porque el zipWith max no tiene el mismo firma como la esperada por map (*2).

+0

gracias. Todo tiene sentido ahora – wajeeh

Cuestiones relacionadas