Pregunta

Estoy tratando de entender lo que el operador está haciendo en este código Haskell:

sumEuler = sum . (map euler) . mkList

El código fuente completo está por debajo.

Mi comprensión

El operador de punto es tomar las dos funciones sum y el resultado de map euler y el resultado de mkList como la entrada.

Pero, sum no es una función es el argumento de la función, ¿verdad?Entonces, ¿qué está pasando aquí?

También, ¿cuál es (map euler) haciendo?

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
¿Fue útil?

Solución

En pocas palabras, . es composición de funciones, 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 " point-free " estilo: se omiten los argumentos de la función. Esto hace que el código sea más claro en muchos casos. (Puede ser difícil asimilarlo la primera vez que lo ve, pero se acostumbrará después de un tiempo. Es un idioma común de Haskell).

Si todavía está confundido, puede ser útil relacionar f con algo como una tubería UNIX. Si la salida de g se convierte en la entrada de h, cuya salida se convierte en la entrada de f < x | g | h, escribiría eso en la línea de comando como |. En Haskell, h . g . f $ x funciona como UNIX map (\x -> x * 2 + 10) [1..10], pero & Quot; al revés & Quot; - (+10) . (*2) <$> [1..10]. Considero que esta notación es bastante útil cuando, por ejemplo, procesa una lista. En lugar de una construcción difícil de manejar como (+10) . (*2) $ 10, podría simplemente escribir <=>. (Y, si solo desea aplicar esa función a un solo valor; es <=>. ¡Consistente!)

El wiki de Haskell tiene un buen artículo con más detalles: http://www.haskell.org/ haskellwiki / Pointfree

Otros consejos

El. 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 código

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 que hay parens alrededor de map euler es porque aclara que se componen 3 funciones: sum , map euler y mkList - map euler es una función única.

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.

El. El operador se utiliza 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 buscar eso.

Lo que hace es que toma una función con digamos dos argumentos, aplica el argumento euler. (mapa euler) ¿verdad? y el resultado es una nueva función, que toma solo un argumento.

suma. (mapa euler). mkList es básicamente una forma elegante de juntar todo eso. Debo decir que mi Haskell está un poco oxidado, pero ¿tal vez puedas armar esa última función tú mismo?

El operador punto se aplica la función a la izquierda (sum) a la salida de la función a la derecha.En su caso, va a encadenar varias funciones - eres el resultado de pasar de mkList a (map euler), y , a continuación, pasar el resultado de que a sum. Este sitio tiene una buena introducción a varios de los conceptos.

  

Operador de puntos en Haskell

     

Estoy tratando de entender qué está haciendo el operador de puntos 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 a map:

map_euler = map euler
sumEuler = sum . map_euler . mkList

Ahora solo tenemos los puntos. ¿Qué se indica con estos puntos?

De la fuente :

(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

Así (.) es el operador de composición .

Componer

En matemáticas, podríamos escribir la composición de funciones, f (x) y g (x), es decir, f (g (x)), como

  

(f & # 8728; g) (x)

que se puede leer " f compuesto con g " ;.

Entonces en Haskell, f & # 8728; g, o f compuesto con g, se puede escribir:

f . g

La composición es asociativa, lo que significa que f (g (h (x))), escrita con el operador de composición, puede omitir los paréntesis sin ninguna ambigüedad.

Es decir, ya que (f & # 8728; g) & # 8728; h es equivalente a f & # 8728; (g & # 8728; h), simplemente podemos escribir f & # 8728; g & # 8728; h.

Dando la vuelta

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))
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top