Question

Quelle est la différence entre le (.) de points et le signe dollar ($)?

Si je comprends bien, ils sont à la fois le sucre syntaxique pour ne pas avoir besoin d'utiliser des parenthèses.

Était-ce utile?

La solution

L'opérateur $ est d'éviter les parenthèses. Tout ce qui apparaît après primera sur tout ce qui vient avant.

Par exemple, disons que vous avez une ligne qui se lit comme suit:

putStrLn (show (1 + 1))

Si vous voulez vous débarrasser de ces parenthèses, l'une des lignes suivantes aussi faire la même chose:

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

Le but principal de l'opérateur . est de ne pas éviter entre parenthèses, mais à des fonctions de la chaîne. Il vous permet de lier la sortie de tout ce qui apparaît sur le droit à l'entrée de tout ce qui apparaît à gauche. Cette habitude entraîne également moins entre parenthèses, mais fonctionne différemment.

Pour en revenir au même exemple:

putStrLn (show (1 + 1))
  1. (1 + 1) ne possède pas d'entrée, et ne peut donc pas être utilisé avec l'opérateur ..
  2. show peut prendre un Int et retourner un String.
  3. putStrLn peut prendre un String et retourner un IO ().

Vous pouvez chaîne show à putStrLn comme ceci:

(putStrLn . show) (1 + 1)

Si c'est trop de parenthèses à votre goût, se débarrasser d'eux avec l'opérateur $:

putStrLn . show $ 1 + 1

Autres conseils

Ils ont des types et différentes définitions différentes:

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 destiné à remplacer l'application de la fonction normale, mais à une autre priorité pour aider à éviter entre parenthèses. (.) est pour composer deux fonctions ensemble pour faire une nouvelle fonction.

Dans certains cas, ils sont interchangeables, mais ce n'est pas vrai en général. L'exemple typique où ils sont est:

f $ g $ h $ x

==>

f . g . h $ x

En d'autres mots dans une chaîne de $s, tous, mais le dernier peut être remplacé par .

Notez également que ($) est la fonction identité spécialisée types de fonction . La fonction d'identité ressemble à ceci:

id :: a -> a
id x = x

Alors que ($) ressemble à ceci:

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

Notez que j'ai intentionnellement ajouté entre parenthèses supplémentaires dans la signature de type.

Les utilisations de ($) peuvent généralement être éliminés en ajoutant entre parenthèses (sauf si l'opérateur est utilisé dans une section). .: f $ g x devient par exemple f (g x).

Les utilisations de (.) sont souvent un peu plus difficile à remplacer; ils ont généralement besoin d'un lambda ou l'introduction d'un paramètre de fonction explicite. Par exemple:

f = g . h

devient

f x = (g . h) x

devient

f x = g (h x)

Hope this helps!

($) permet à des fonctions devant être enchaînées sans ajouter de parenthèses pour contrôler l'ordre d'évaluation:

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

Prelude> head $ tail "asdf"
's'

L'opérateur crée une Compose (.) nouvelle fonction sans spécifier les arguments:

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

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

L'exemple ci-dessus est sans doute d'illustration, mais ne montre pas vraiment la possibilité d'utiliser la composition. Voici une autre analogie:

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

Si nous utilisons une troisième fois, nous pouvons éviter de nommer en utilisant un lambda:

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

Enfin, la composition nous permet d'éviter le lambda:

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

La version courte et douce:

  • ($) appelle la fonction qui est son argument à la main gauche sur la valeur qui est son argument de droit.
  • (.) compose la fonction qui est son argument de gauche sur la fonction qui est son argument de droit.

Une application qui est utile et m'a pris un certain temps pour comprendre de la description très court à vous apprendre haskell: Depuis:

f $ x = f x

et la parenthèse droite d'une expression contenant un opérateur infixe le convertit en une fonction de préfixe, on peut écrire ($ 3) (4+) analogue à (++", world") "hello".

Pourquoi quelqu'un faire cela? Pour les listes de fonctions, par exemple. Les deux:

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

et

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

sont plus courtes que map (\x -> x ++ ", world") ... ou map (\f -> f 3) .... De toute évidence, les dernières variantes seraient plus lisibles pour la plupart des gens.

... ou vous pouvez éviter les constructions de . et $ en utilisant pipelining :

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

C'est une fois que vous avez ajouté dans la fonction d'aide:

(|>) x y = y x

Une excellente façon d'en savoir plus sur quoi que ce soit (une fonction) est de se rappeler que tout est une fonction! Ce mantra général aide, mais dans des cas particuliers comme les opérateurs, il aide à se rappeler cette petite astuce:

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

et

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

Rappelez-vous simplement d'utiliser :t généreusement, et envelopper vos opérateurs ()!

Ma règle est simple (je suis débutant aussi):

  • ne pas utiliser . si vous voulez passer le paramètre (appeler la fonction), et
  • ne pas utiliser $ s'il n'y a pas encore paramètre (composer une fonction)

C'est

show $ head [1, 2]

mais jamais:

show . head [1, 2]
  

Haskell: différence entre . (dot) et $ (signe dollar)

     

Quelle est la différence entre le point (.) et le signe dollar ($) ?. Si je comprends bien, ils sont à la fois le sucre syntaxique pour ne pas avoir besoin d'utiliser des parenthèses.

Ils sont pas sucre syntaxique pour ne pas avoir besoin d'utiliser entre parenthèses - ce sont des fonctions, - infixé, on peut les appeler ainsi les opérateurs.

Compose, (.), et quand l'utiliser.

(.) est la fonction de composition. Donc,

result = (f . g) x

est la même que la construction d'une fonction qui transmet le résultat de son argument passé à g à f.

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

Utilisez (.) lorsque vous n'avez pas les arguments disponibles pour passer aux fonctions que vous souhaitez composer.

Appliquer associative droite, ($), et quand l'utiliser

($) est une fonction de transformation droit associatif avec priorité faible liaison. Donc, il calcule simplement les choses à droite de celui-ci en premier. Ainsi,

result = f $ g x

est le même que cela, la procédure (qui compte depuis Haskell est évalué paresseusement, il commencera à évaluer f premier):

h = f
g_x = g x
result = h g_x

ou de façon plus concise:

result = f (g x)

Utilisez ($) lorsque vous avez toutes les variables à évaluer avant d'appliquer la fonction précédente au résultat.

On peut le voir en lisant la source pour chaque fonction.

Lire la source

Voici le Source (.):

-- | 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)

Et voici la ($):

-- | 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

Conclusion

composition utilisation lorsque vous n'avez pas besoin d'évaluer immédiatement la fonction. Peut-être vous voulez passer la fonction qui résulte de la composition à une autre fonction.

Utilisez l'application lorsque vous fournissez tous les arguments pour une évaluation complète.

Donc, pour notre exemple, il serait préférable de le faire sémantiquement

f $ g x

quand nous avons x (ou plutôt, les arguments de g) et faites:

f . g

quand nous ne le faisons pas.

Je pense qu'un court exemple de l'endroit où vous utiliseriez . et non $ contribuerait à clarifier les choses.

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

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

Notez que times6 est une fonction qui est créé à partir de la composition de fonctions.

Toutes les autres réponses sont assez bonnes. Mais il y a un détail d'utilisation important sur la façon dont GHC traite $, que le vérificateur de type GHC permet instatiarion avec rang supérieur / types quantifiés. Si vous regardez le type de $ id par exemple, vous trouverez que ça va prendre une fonction dont l'argument est lui-même une fonction polymorphique. Des petites choses comme ça ne sont pas donnés la même flexibilité avec un opérateur bouleversé équivalent. (Cela me fait me demande si en fait $! Mérite le même traitement ou non)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top