pattern - io function haskell




Qual é a diferença entre.(ponto) e $(cifrão)? (8)

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 usar parênteses.

Eles não são açúcar sintático para não precisar usar parênteses - eles são funções, - infixados, assim podemos chamá-los de operadores.

(.) é a função de composição. assim

result = (f . g) x

é o mesmo que construir uma função que passa o resultado de seu argumento passado para g em para f .

h = \x -> f (g x)
result = h x

($) é uma função de aplicação associativa à direita com baixa precedência de ligação. Por isso, calcula as coisas primeiro à direita. Portanto,

result = f $ g x

é o mesmo que isso, processualmente (o que importa desde que Haskell é avaliado preguiçosamente, ele começará a avaliar f primeiro):

h = f
g_x = g x
result = h g_x

ou mais concisa:

result = f (g x)

Podemos ver isso lendo a fonte para cada função.

Leia a fonte

Aqui está a source para (.) :

-- | 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 source 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) [email protected],
-- or @'Data.List.zipWith' ('$') fs [email protected]
{-# INLINE ($) #-}
($)                     :: (a -> b) -> a -> b
f $ x                   =  f x

Quando usar:

Use a composição quando você não precisar avaliar imediatamente a função. Talvez você queira passar a função que resulta da composição para outra função.

Use o aplicativo quando você estiver fornecendo todos os argumentos para avaliação completa.

Então, para o nosso exemplo, seria semanticamente preferível fazer

f $ g x

quando temos x (ou melhor, argumentos de g ), e fazemos:

f . g

quando nós nã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 usar parênteses.


... ou você poderia evitar o . e $ construções usando pipelining :

third xs = xs |> tail |> tail |> head

Isso é depois que você adicionou na função auxiliar:

(|>) x y = y x

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

($) destina-se a substituir o aplicativo de função normal, mas com uma precedência diferente para ajudar a evitar parênteses. (.) é para compor duas funções juntas 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 estão é:

f $ g $ h $ x

==>

f . g . h $ x

Em outras palavras, em uma cadeia de $ s, todos, exceto o final, podem ser substituídos por .


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

Observe que times6 é uma função criada a partir da composição da função.


O operador $ é para evitar parênteses. Qualquer coisa que apareça depois disso terá precedência sobre qualquer coisa que venha antes.

Por exemplo, digamos que você tenha uma linha que diz:

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 é para evitar parênteses, mas para funções de cadeia. Ele permite amarrar a saída do que aparece à direita na entrada do que aparece à esquerda. Isso geralmente também resulta em menos parênteses, mas funciona de maneira diferente.

Voltando ao mesmo exemplo:

putStrLn (show (1 + 1))
  1. (1 + 1) não tem uma entrada e, portanto, não pode ser usado com o . operador.
  2. show pode pegar um Int e retornar um String .
  3. putStrLn pode pegar uma String e retornar um IO () .

Você pode encadear show para putStrLn assim:

(putStrLn . show) (1 + 1)

Se isso é muitos parênteses para o seu gosto, se livrar deles com o operador $ :

putStrLn . show $ 1 + 1

Observe também que ($) é a função de identidade especializada em tipos de função . A função de identidade é assim:

id :: a -> a
id x = x

Enquanto ($) parece com isto:

($) :: (a -> b) -> (a -> b)
($) = id

Observe que eu intencionalmente adicionei parênteses extras na assinatura de tipo.

Usos de ($) geralmente podem ser eliminados pela adição de parênteses (a menos que o operador seja usado em uma seção). Por exemplo: f $ gx se torna f (gx) .

Usos de (.) São geralmente um pouco mais difíceis de serem substituídos; eles geralmente precisam de um lambda ou da introdução de um parâmetro de função explícito. Por exemplo:

f = g . h

torna-se

f x = (g . h) x

torna-se

f x = g (h x)

Espero que isto ajude!


Um aplicativo que é útil e me levou algum tempo para descobrir a partir da descrição muito curta em aprender um haskell : Desde:

f $ x = f x

e parênteses ao lado direito de uma expressão que contém um operador infixo o converte em uma função de prefixo, pode-se escrever ($ 3) (4+) análogo a (++", 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 menores que map (\x -> x ++ ", world") ... ou map (\f -> f 3) ... Obviamente, as últimas variantes seriam mais legíveis para a maioria das pessoas.


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, ajuda a lembrar desse pequeno truque:

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

e

:t ($)
($) :: (a -> b) -> a -> b

Apenas lembre-se de usar :t liberalmente, e envolva seus operadores em () !





function-composition