Dot Operador em Haskell: precisar de mais explicações
-
08-07-2019 - |
Pergunta
Eu estou tentando entender o que o operador ponto está fazendo neste código Haskell:
sumEuler = sum . (map euler) . mkList
O código fonte inteira está abaixo.
O meu entendimento
O operador ponto está levando a duas funções sum
eo resultado da map euler
eo resultado da mkList
como entrada.
Mas, sum
não é uma função é o argumento da função, certo? Então, o que está acontecendo aqui?
Além disso, o que é (map euler)
fazendo?
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
Solução
Simplificando, .
é composição de função, assim como em matemática:
f (g x) = (f . g) x
No seu caso, você está criando uma nova função, sumEuler
que também poderia ser definido assim:
sumEuler x = sum (map euler (mkList x))
O estilo no seu exemplo é chamado de "livre-point" estilo - os argumentos para a função são omitidos. Isto faz para código mais claro em muitos casos. (Pode ser difícil para Grokar a primeira vez que você vê-lo, mas você vai se acostumar a ele depois de um tempo. É uma expressão comum Haskell.)
Se você ainda está confuso, ele pode ajudar a relacionar .
para algo como um tubo de UNIX. Se a saída do f
torna-se a entrada do g
, cuja produção se torna a entrada do h
, você escreveria que na linha de comando como f < x | g | h
. Em Haskell, .
funciona como o |
UNIX, mas "para trás" - h . g . f $ x
. Acho que esta notação a ser bastante útil quando, por exemplo, o processamento de uma lista. Em vez de algumas obras de difícil controle como map (\x -> x * 2 + 10) [1..10]
, você poderia simplesmente escrever (+10) . (*2) <$> [1..10]
. (E, se você quiser aplicar apenas essa função para um único valor;.!-Lo de (+10) . (*2) $ 10
Consistente)
O wiki Haskell tem um bom artigo com mais algum detalhe: http://www.haskell.org/ haskellwiki / pointfree
Outras dicas
A. operador compõe funções. Por exemplo,
a . b
Onde a e b são funções é um novo Função que corre b em seus argumentos, então uma base nesses resultados. Seu código
sumEuler = sum . (map euler) . mkList
é exatamente o mesmo que:
sumEuler myArgument = sum (map euler (mkList myArgument))
mas espero mais fácil de ler. A razão existem parênteses em torno de Mapa Euler é porque torna mais claro que há 3 funções sendo composta: sum , Mapa Euler e mkList -. mapa Euler é uma única função
sum
é uma função no Prelude Haskell, não um argumento para sumEuler
. Ele tem o tipo
Num a => [a] -> a
O .
operador composição de função tem tipo
(b -> c) -> (a -> b) -> a -> c
Portanto, temos
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
Note que Int
é uma instância de Num
.
A. operador é utilizado para a composição de função. Assim como matemática, se você tem que funções f (x) eg (x) f. g torna-se f (G (x)).
mapa é um built-in função que aplica uma função a uma lista. Colocando a função em parênteses a função é tratado como um argumento. Um termo para isso é currying . Você deve olhar isso.
O que é que é que ele tem uma função com digamos dois argumentos, aplica-se a Euler argumento. (Mapa Euler) certo? eo resultado é uma nova função, que tem apenas um argumento.
sum. (Mapa Euler). mkList é basicamente uma maneira extravagante de colocar tudo isso junto. Devo dizer, meu Haskell é um pouco enferrujado, mas talvez você possa colocar essa última função junto a si mesmo?
O operador ponto aplica a função à esquerda (sum
) para a saída da função no lado direito. No seu caso, você está encadeamento várias funções em conjunto - você está passando o resultado de mkList
para (map euler)
, e em seguida, passando o resultado de que a sum
.
Este site tem uma boa introdução a vários dos conceitos.
Dot Operador em Haskell
Eu estou tentando entender o que o operador ponto está fazendo neste código Haskell:
sumEuler = sum . (map euler) . mkList
Resposta curta ??h2>
código equivalente sem pontos, que é apenas
sumEuler = \x -> sum ((map euler) (mkList x))
ou sem a lambda
sumEuler x = sum ((map euler) (mkList x))
porque o ponto (.) Indica composição de função.
resposta mais longa
Primeiro, vamos simplificar a aplicação parcial de euler
para map
:
map_euler = map euler
sumEuler = sum . map_euler . mkList
Agora só temos os pontos. O que é indicado por esses pontos?
De a fonte :
(.) :: (b -> c) -> (a -> b) -> a -> c (.) f g = \x -> f (g x)
Assim (.)
é o componha operador .
Compose
Em matemática, podemos escrever a composição de funções, f (x) eg (x), isto é, f (g (x)), como
(f ° g) (x)
que pode ser lido "f composta com g".
Assim, em Haskell, f ° g ou f composta com g, pode ser escrita:
f . g
Composição é associativa, o que significa que f (g (h (x))), escrito com o operador de composição, pode deixar de fora os parênteses sem qualquer ambiguidade.
Isto é, uma vez que (f ° g) ° h é equivalente a f ° (g h °), podemos simplesmente escrever f ° g ° h.
Circular volta ??h2>
Circular de volta ao nosso simplificação anteriormente, esta:
sumEuler = sum . map_euler . mkList
apenas significa que sumEuler
é uma composição unapplied dessas funções:
sumEuler = \x -> sum (map_euler (mkList x))