sobre - multiplicar haskell
Operador de puntos en Haskell: necesito más explicaciones (6)
Operador de puntos en Haskell
Estoy tratando de entender lo que el operador de punto está haciendo en este código Haskell:
sumEuler = sum . (map euler) . mkList
Respuesta corta
Código equivalente sin puntos, eso es solo
sumEuler = /x -> sum ((map euler) (mkList x))
o sin la lambda
sumEuler x = sum ((map euler) (mkList x))
porque el punto (.) indica la composición de la función.
Respuesta más larga
Primero, simplifiquemos la aplicación parcial de euler
para map
:
map_euler = map euler
sumEuler = sum . map_euler . mkList
Ahora solo tenemos los puntos. ¿Qué es indicado por estos puntos?
De la fuente :
(.) :: (b -> c) -> (a -> b) -> a -> c (.) f g = /x -> f (g x)
Por lo tanto, (.)
Es el operador de redacción .
Componer
En matemáticas, podríamos escribir la composición de funciones, f (x) yg (x), es decir, f (g (x)), como
(f ∘ g) (x)
que puede leerse "f compuesto con g".
Entonces en Haskell, f ∘ g, o f compuesto con g, puede escribirse:
f . g
La composición es asociativa, lo que significa que f (g (h (x))), escrita con el operador de composición, puede omitir el paréntesis sin ninguna ambigüedad.
Es decir, dado que (f ∘ g) ∘ h es equivalente a f ∘ (g ∘ h), simplemente podemos escribir f ∘ g ∘ h.
Dando vueltas
Volviendo a nuestra simplificación anterior, esto:
sumEuler = sum . map_euler . mkList
solo significa que sumEuler
es una composición no aplicada de esas funciones:
sumEuler = /x -> sum (map_euler (mkList x))
Estoy tratando de entender lo que el operador de punto está haciendo en este código Haskell:
sumEuler = sum . (map euler) . mkList
El código fuente completo está debajo.
Mi entendimiento
El operador punto está tomando las dos funciones sum
y el resultado de map euler
y el resultado de mkList
como entrada.
Pero, la sum
no es una función, es el argumento de la función, ¿verdad? Entonces, ¿qué está pasando aquí?
Además, ¿qué está haciendo (map euler)
?
Código
mkList :: Int -> [Int]
mkList n = [1..n-1]
euler :: Int -> Int
euler n = length (filter (relprime n) (mkList n))
sumEuler :: Int -> Int
sumEuler = sum . (map euler) . mkList
El operador de punto aplica la función de la izquierda ( sum
) a la salida de la función de la derecha. En tu caso, estás encadenando varias funciones juntas: pasas el resultado de mkList
a (map euler)
y luego mkList
el resultado de eso a la sum
. Este sitio tiene una buena introducción a varios de los conceptos.
En pocas palabras,. es la composición de la función, al igual que en matemáticas:
f (g x) = (f . g) x
En su caso, está creando una nueva función, sumEuler
que también podría definirse así:
sumEuler x = sum (map euler (mkList x))
El estilo en su ejemplo se llama estilo "sin puntos": los argumentos para la función se omiten. Esto hace que el código sea más claro en muchos casos. (Puede ser difícil asimilar la primera vez que lo ves, pero te acostumbrarás después de un tiempo. Es un idioma común de Haskell).
Si todavía está confundido, puede ayudar a relacionarse .
a algo así como una tubería UNIX. Si la salida de f
convierte en la entrada de g
, cuya salida se convierte en la entrada de h
, escribiría eso en la línea de comando como f < x | g | h
f < x | g | h
f < x | g | h
. En Haskell,. funciona como el UNIX |
, pero "al revés" - h . g . f $ x
h . g . f $ x
h . g . f $ x
. Creo que esta notación es bastante útil cuando, por ejemplo, procesamos una lista. En lugar de una construcción difícil de manejar como el map (/x -> x * 2 + 10) [1..10]
, podría escribir (+10) . (*2) <$> [1..10]
(+10) . (*2) <$> [1..10]
. (Y, si solo desea aplicar esa función a un único valor, es (+10) . (*2) $ 10
¡Consistente!)
La wiki de Haskell tiene un buen artículo con más detalles: http://www.haskell.org/haskellwiki/Pointfree
Los . el operador compone funciones Por ejemplo,
a . b
Donde a y b son funciones es una nueva función que ejecuta b en sus argumentos, luego a en esos resultados. Tu codigo
sumEuler = sum . (map euler) . mkList
es exactamente lo mismo que:
sumEuler myArgument = sum (map euler (mkList myArgument))
pero espero que sea más fácil de leer. La razón por la cual hay paréntesis alrededor de map euler es porque hace más claro que hay 3 funciones compuestas: sum , map euler y mkList - map euler es una función única.
Los . el operador se usa para la composición de funciones. Al igual que las matemáticas, si tiene que usar las funciones f (x) y g (x) f. g se convierte en f (g (x)).
map es una función incorporada que aplica una función a una lista. Al poner la función entre paréntesis, la función se trata como un argumento. Un término para esto es currying . Deberías mirar eso.
Lo que hace es que toma una función con decir dos argumentos, aplica el argumento euler. (mapa de euler) ¿verdad? y el resultado es una nueva función, que solo requiere un argumento.
suma (mapa euler). mkList es básicamente una forma elegante de armar todo eso. Debo decir que mi Haskell está un poco oxidado, pero ¿quizás usted mismo puede armar esa última función?
sum
es una función en el preludio de Haskell, no un argumento para sumEuler
. Tiene el tipo
Num a => [a] -> a
El operador de composición de funciones .
tiene tipo
(b -> c) -> (a -> b) -> a -> c
Entonces tenemos
sum :: Num a => [a] -> a
map :: (a -> b) -> [a] -> [b]
euler :: Int -> Int
mkList :: Int -> [Int]
(map euler) :: [Int] -> [Int]
(map euler) . mkList :: Int -> [Int]
sum . (map euler) . mkList :: Int -> Int
Tenga en cuenta que Int
es una instancia de Num
.