Qual é a diferença entre . (Ponto) e $ (cifrão)?
-
06-09-2019 - |
Pergunta
Qual é a diferença entre o (.)
ponto eo ($)
cifrão?
Pelo que entendi, ambos são açúcar sintático para não precisar parênteses uso.
Solução
O operador $
é para evitar parênteses. Qualquer coisa que aparece depois que ele terá precedência sobre qualquer coisa que vem antes.
Por exemplo, digamos que você tem uma linha que lê:
putStrLn (show (1 + 1))
Se você quiser se livrar desses parênteses, qualquer uma das seguintes linhas também faria a mesma coisa:
putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1
O objetivo principal do operador .
não é parênteses evitar, mas para funções da cadeia. Ele permite que você amarrar a saída de qualquer aparece à direita para a entrada de qualquer aparece no lado esquerdo. Isso normalmente também resulta em menos parênteses, mas funciona de forma diferente.
Voltando ao mesmo exemplo:
putStrLn (show (1 + 1))
-
(1 + 1)
não tem uma entrada e, portanto, não pode ser usado com o operador.
. -
show
pode tomar umInt
e retornar umString
. -
putStrLn
pode tomar umString
e retornar umIO ()
.
Você pode show
cadeia para putStrLn
assim:
(putStrLn . show) (1 + 1)
Se isso é demasiado muitos parênteses para o seu gosto, se livrar deles com o operador $
:
putStrLn . show $ 1 + 1
Outras dicas
Eles têm diferentes tipos e diferentes definições:
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)
infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x
($)
se destina a substituir a aplicação de função normal, mas em uma precedência diferente para parênteses ajudam a evitar. (.)
é para compor duas funções em conjunto para fazer uma nova função.
Em alguns casos, eles são intercambiáveis, mas isso não é verdade em geral. O exemplo típico onde eles estão é:
f $ g $ h $ x
==>
f . g . h $ x
Em outras palavras, em uma cadeia de $
s, todos, mas o último pode ser substituído por .
Observe também que ($)
é a função identidade especializada para funcionar tipos . A função identidade esta aparência:
id :: a -> a
id x = x
Enquanto ($)
esta aparência:
($) :: (a -> b) -> (a -> b)
($) = id
Note que eu adicionei intencionalmente parênteses adicionais na assinatura de tipo.
Utilizações de ($)
geralmente pode ser eliminado através da adição de parênteses (a menos que o operador é utilizado em uma secção). Ex .: f $ g x
se torna f (g x)
.
Usos de (.)
são muitas vezes um pouco mais difícil de substituir; eles geralmente precisam de um lambda ou a introdução de um parâmetro de função explícita. Por exemplo:
f = g . h
se torna
f x = (g . h) x
se torna
f x = g (h x)
Espero que isso ajude!
($)
permite funções a serem encadeadas sem adicionar parênteses para ordem de avaliação de controle:
Prelude> head (tail "asdf")
's'
Prelude> head $ tail "asdf"
's'
O (.)
operador compor cria uma nova função sem especificar os argumentos:
Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'
Prelude> let second = head . tail
Prelude> second "asdf"
's'
O exemplo acima é sem dúvida ilustrativa, mas não realmente mostrar a conveniência de usar composição. Aqui está outra analogia:
Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"
Se nós usamos somente terceira vez, podemos evitar nomeá-lo usando um lambda:
Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"
Finalmente, a composição nos permite evitar o lambda:
Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"
A versão curta e doce:
-
($)
chama a função que é seu argumento do lado esquerdo sobre o valor que é seu argumento do lado direito. -
(.)
compõe a função que é seu argumento do lado esquerdo na função que é seu argumento do lado direito.
Uma aplicação que é útil e me levou algum tempo para descobrir a partir da breve descrição em aprender uma haskell : Desde:
f $ x = f x
e parenthesizing o lado direito de uma expressão contendo um infixas convertidos operador de TI a uma função de prefixo, um pode escrever ($ 3) (4+)
análogo ao (++", world") "hello"
.
Por que alguém faria isso? Para listas de funções, por exemplo. Ambos:
map (++", world") ["hello","goodbye"]`
e
map ($ 3) [(4+),(3*)]
são mais curtos do que map (\x -> x ++ ", world") ...
ou map (\f -> f 3) ...
. Obviamente, as últimas variantes seria mais legível para a maioria das pessoas.
... ou você poderia evitar as construções .
e $
usando pipelining :
third xs = xs |> tail |> tail |> head
Isso é depois que você adicionou na função auxiliar:
(|>) x y = y x
Uma ótima maneira de aprender mais sobre qualquer coisa (qualquer função) é lembrar que tudo é uma função! Esse mantra geral ajuda, mas em casos específicos, como operadores, que ajuda a lembrar este pequeno truque:
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
e
:t ($)
($) :: (a -> b) -> a -> b
Apenas lembre-se de usar :t
liberalmente, e envolver seus operadores em ()
!
A minha regra é simples (eu sou novato também):
- Não uso
.
se você quiser passar o parâmetro (chamar a função), e - Não uso
$
se não houver nenhum parâmetro ainda (compor uma função)
Ou seja
show $ head [1, 2]
mas nunca:
show . head [1, 2]
Haskell: diferença entre
.
(ponto) e$
(cifrão)Qual é a diferença entre o
(.)
ponto eo($)
cifrão ?. Pelo que entendi, ambos são açúcar sintático para não precisar parênteses uso.
Eles são não açúcar sintático para não precisar usar parênteses - são funções, - infixada, assim podemos chamá-los de operadores.
Compose, (.)
, e quando usá-lo.
(.)
é a função de composição. Então
result = (f . g) x
é o mesmo que construir uma função que passa o resultado do seu argumento passado para g
para f
.
h = \x -> f (g x)
result = h x
Use (.)
quando você não tem os argumentos disponíveis para passar para as funções que deseja compor.
Right associativa aplicar, ($)
, e quando usá-lo
($)
é um associativos à direita aplicar a função com baixa precedência vinculativo. Então ele apenas calcula as coisas à direita do primeiro. Assim,
result = f $ g x
é o mesmo que este, processualmente (que assuntos desde Haskell é avaliada preguiçosamente, ele vai começar a avaliar f
primeiro):
h = f
g_x = g x
result = h g_x
ou mais concisa:
result = f (g x)
Use ($)
quando você tem todas as variáveis ??para avaliar antes de aplicar a função anterior para o resultado.
Podemos ver isso lendo a fonte para cada função.
Leia a fonte
-- | Function composition.
{-# INLINE (.) #-}
-- Make sure it has TWO args only on the left, so that it inlines
-- when applied to two functions, even if there is no final argument
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)
E aqui está a href="http://hackage.haskell.org/package/base-4.10.0.0/docs/src/GHC.Base.html#%24" rel="nofollow noreferrer"> fonte para ($)
:
-- | Application operator. This operator is redundant, since ordinary
-- application @(f x)@ means the same as @(f '$' x)@. However, '$' has
-- low, right-associative binding precedence, so it sometimes allows
-- parentheses to be omitted; for example:
--
-- > f $ g $ h x = f (g (h x))
--
-- It is also useful in higher-order situations, such as @'map' ('$' 0) xs@,
-- or @'Data.List.zipWith' ('$') fs xs@.
{-# INLINE ($) #-}
($) :: (a -> b) -> a -> b
f $ x = f x
Conclusão
Use composição quando você não precisa imediatamente avaliar a função. Talvez você quer passar a função que resulta da composição para outra função.
Use aplicação quando estamos fornecendo todos os argumentos para avaliação completa.
Assim, para nosso exemplo, seria semanticamente preferível fazer
f $ g x
quando temos x
(ou melhor, os argumentos de g
), e fazer:
f . g
quando não o fazemos.
Eu acho que um pequeno exemplo de onde você usaria .
e não $
ajudaria a esclarecer as coisas.
double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c
Note que times6
é uma função que é criada a partir de composição de função.
Todas as outras respostas são muito bons. Mas há um detalhe importante usabilidade sobre como ghc trata $, que o verificador de tipos ghc permite instatiarion com / tipos quantificados maior pontuação. Se você olhar para o tipo de $ id
por exemplo, você vai encontrá-lo vai ter uma função cujo argumento é em si uma função polimórfica. Pequenas coisas como essa não são dadas a mesma flexibilidade com um operador chateado equivalente. (Isso realmente me faz pensar se $! Merece o mesmo tratamento ou não)