Pregunta

¿Cuál es la diferencia entre el punto y el (.) ($) signo del dólar?

A mi entender, los dos son azúcar sintáctica para no tener que utilizar paréntesis.

¿Fue útil?

Solución

El operador $ es para evitar paréntesis. Cualquier cosa que aparece después de que tendrá prioridad sobre cualquier cosa que viene antes.

Por ejemplo, digamos que usted tiene una línea que dice:

putStrLn (show (1 + 1))

Si desea deshacerse de esos paréntesis, cualquiera de las siguientes líneas también haría lo mismo:

putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1

El propósito principal del operador . no es evitar paréntesis, pero a funciones de la cadena. Le permite atar la salida de todo lo que aparece a la derecha a la entrada de lo que aparece a la izquierda. Esto por lo general también se traduce en un menor número de paréntesis, pero funciona de forma diferente.

Volviendo al mismo ejemplo:

putStrLn (show (1 + 1))
  1. (1 + 1) no tiene una entrada, y por lo tanto no se puede utilizar con el operador ..
  2. show puede tomar una Int y devolver una String.
  3. putStrLn puede tomar un String y devolver una IO ().

Puede show cadena para putStrLn como esto:

(putStrLn . show) (1 + 1)

Si eso es demasiados paréntesis para su gusto, deshacerse de ellos con el operador $:

putStrLn . show $ 1 + 1

Otros consejos

Tienen diferentes tipos y diferentes definiciones:

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

infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x

($) está destinada a sustituir la aplicación función normal pero a una prioridad diferente para ayudar a evitar paréntesis. (.) es para la composición de dos funciones juntas para hacer una nueva función.

En algunos casos son intercambiables, pero esto no es cierto en general. El ejemplo típico donde están es:

f $ g $ h $ x

==>

f . g . h $ x

En otras palabras en una cadena de $s, todos excepto la final puede ser sustituido por .

Tenga en cuenta también que ($) es la función identidad especializado para funcionar tipos . La función identidad es el siguiente:

id :: a -> a
id x = x

Mientras ($) se ve así:

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

Tenga en cuenta que he añadido intencionadamente paréntesis adicionales en la declaración de tipo.

Usos de ($) por lo general se pueden eliminar mediante la adición de paréntesis (a menos que el operador se utiliza en una sección). Ej .: f $ g x convierte f (g x).

Usos de (.) son a menudo un poco más difícil de sustituir; por lo general necesitan un lambda o la introducción de un parámetro de la función explícita. Por ejemplo:

f = g . h

se convierte

f x = (g . h) x

se convierte

f x = g (h x)

Espero que esto ayude!

funciones

($) permite ser encadenados juntos sin añadir paréntesis para controlar el orden de evaluación:

Prelude> head (tail "asdf")
's'

Prelude> head $ tail "asdf"
's'

El (.) operador de composición crea una nueva función sin especificar los argumentos:

Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'

Prelude> let second = head . tail
Prelude> second "asdf"
's'

El ejemplo anterior es posiblemente ilustrativo, pero en realidad no muestran la conveniencia de utilizar la composición. Aquí hay otra analogía:

Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"

Si solamente usamos tercera vez, podemos evitar nombrarlo mediante el uso de un lambda:

Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"

Finalmente, la composición nos permite evitar la lambda:

Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"

La versión corta y dulce:

  • ($) llama a la función que es su argumento de la izquierda en el valor, que es su argumento de la derecha.
  • (.) compone la función que es su argumento de la izquierda en la función que es su argumento de la derecha.

Una aplicación que es útil y me tomó un poco de tiempo para averiguar de la descripción muy breve por lo que aprender Haskell : Desde:

f $ x = f x

y parenthesizing el lado derecho de una expresión que contiene un operador infijo la convierte en una función de prefijo, uno puede escribir ($ 3) (4+) análoga a (++", world") "hello".

¿Por qué alguien haría esto? Para listas de funciones, por ejemplo. Ambos:

map (++", world") ["hello","goodbye"]`

y

map ($ 3) [(4+),(3*)]

son más cortos que map (\x -> x ++ ", world") ... o map (\f -> f 3) .... Obviamente, las últimas variantes serían más fácil de leer para la mayoría de la gente.

... o usted podría evitar las construcciones . y $ mediante el uso de canalización

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

Eso es después de añadir la función auxiliar:

(|>) x y = y x

Una gran manera de aprender más acerca de nada (ninguna función) es recordar que todo es una función! Ese mantra general de ayuda, pero en casos específicos como los operadores, es útil recordar este pequeño truco:

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

y

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

Sólo recuerde utilizar :t liberalmente, y envolver sus operadores en ()!

Mi regla es simple (soy principiante también):

  • No utilice . si quieres pasar el parámetro (llamar a la función), y
  • No utilice $ si no hay parámetro todavía (componer una función)

Es

show $ head [1, 2]

pero nunca:

show . head [1, 2]
  

Haskell: diferencia entre . (punto) y $ (signo de dólar)

     

¿Cuál es la diferencia entre el punto y el (.) ($) signo de dólar ?. Como yo lo entiendo, ambos son azúcar sintáctica para no tener que utilizar paréntesis.

Son no de azúcar sintáctica para no tener que utilizar paréntesis - se trata de funciones, - infijada, por lo tanto podemos llamarlos operadores.

Componer, (.), y cuándo usarlo.

(.) es la función de composición. Así

result = (f . g) x

es la misma que la construcción de una función que pasa el resultado de su argumento pasado a g a f.

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

Uso (.) cuando usted no tiene los argumentos disponibles para pasar a las funciones que desea componer.

aplicar

asociativo derecho, ($), y cuándo usarlo

($) es una función asociativa por la derecha aplica con baja prioridad de unión. Por lo tanto, sólo calcula las cosas a la derecha del primero. Por lo tanto,

result = f $ g x

es el mismo que este, de procedimiento (que importa ya que Haskell se evalúa con pereza, que comenzará a evaluar f primero):

h = f
g_x = g x
result = h g_x

o más concisamente:

result = f (g x)

Utilice ($) cuando se tiene todas las variables para evaluar antes de aplicar la función anterior para el resultado.

Podemos ver esto mediante la lectura de la fuente para cada función.

Lea la fuente

Aquí está la fuente 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)

Y aquí está el fuente 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

Conclusión

Uso composición cuando no es necesario para evaluar la función de inmediato. Tal vez usted quiere pasar a la función que resulta de la composición a otra función.

El uso de la aplicación cuando está suministrando todos los argumentos para una evaluación completa.

Así que para nuestro ejemplo, sería preferible hacerlo semánticamente

f $ g x

cuando tenemos x (o más bien, los argumentos de g), y hacemos:

f . g

cuando no lo hacemos.

Creo que un ejemplo corto de donde se utilizaría . y no $ ayudaría a aclarar las cosas.

double x = x * 2
triple x = x * 3
times6 = double . triple

:i times6
times6 :: Num c => c -> c

Tenga en cuenta que times6 es una función que se crea a partir de la composición de funciones.

Todas las otras respuestas son bastante buenos. Pero hay un detalle importante usabilidad acerca de cómo trata a GHC $, que el tipo de corrector GHC permite instatiarion con RANK / cuantificados tipos más altos. Si nos fijamos en el tipo de $ id por ejemplo, encontrará que va a tener una función cuyo argumento es en sí mismo una función polimórfica. Las pequeñas cosas como que no se les da la misma flexibilidad con un operador molesto equivalente. (En realidad, esto hace que me pregunte si $! Merece el mismo tratamiento o no)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top