Quelle est la différence entre . (point) et $ (signe dollar)?

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

L’opérateur $ d’éviter les parenthèses. Tout ce qui apparaîtra par la suite aura préséance sur tout ce qui précède.

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 ferait la même chose:

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

Le but premier de la . L’opérateur n’est pas pour éviter les parenthèses, mais pour enchaîner les fonctions. Il vous permet de lier la sortie de ce qui apparaît à droite à l’entrée de ce qui apparaît à gauche. Cela se traduit généralement par moins de parenthèses, mais fonctionne différemment.

Revenons au même exemple:

 putStrLn (show (1 + 1)) 
  1. (1 + 1) n’a pas d’entrée et ne peut donc pas être utilisé avec le . opérateur.
  2. show peut prendre un Int et retourner une Ssortingng .
  3. putStrLn peut prendre une Ssortingng et retourner un IO () .

Vous pouvez enchaîner show à putStrLn comme ceci:

 (putStrLn . show) (1 + 1) 

Si c’est trop de parenthèses à votre goût, éliminez-les avec l’opérateur $ :

 putStrLn . show $ 1 + 1 

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

 infixr 9 . (.) :: (b -> c) -> (a -> b) -> (a -> c) (f . g) x = f (gx) infixr 0 $ ($) :: (a -> b) -> a -> b f $ x = fx 

($) est destiné à remplacer l’application de fonction normale mais à une priorité différente pour éviter les parenthèses. (.) de composer deux fonctions ensemble pour créer une nouvelle fonction.

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

 f $ g $ h $ x 

==>

 f . g . h $ x 

En d’autres termes, dans une chaîne de $ s, tout sauf le final peut être remplacé par .

Notez également que ($) est la fonction d’identité spécialisée pour les types de fonctions . 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é des parenthèses supplémentaires dans la signature de type.

Les utilisations de ($) peuvent généralement être éliminées en ajoutant des parenthèses (à moins que l’opérateur ne soit utilisé dans une section). Par exemple: f $ gx devient f (gx) .

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

 f = g . h 

devient

 fx = (g . h) x 

devient

 fx = g (hx) 

J’espère que cela t’aides!

($) permet de chaîner les fonctions sans append de parenthèses pour contrôler l’ordre d’évaluation:

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

L’opérateur de composition (.) Crée une 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 illustratif, mais ne montre pas vraiment la facilité d’utilisation de la composition. Voici une autre analogie:

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

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

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

Enfin, la composition 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 de gauche sur la valeur qui est son argument de droite.
  • (.) compose la fonction qui est son argument de gauche sur la fonction qui est son argument de droite.

Une application utile et qui m’a pris du temps pour comprendre, à partir de la très brève description, vous apprendre un haskell : Depuis:

 f $ x = fx 

et la parenthèse du côté droit d’une expression contenant un opérateur infixe la convertit en une fonction de préfixe, on peut écrire ($ 3) (4+) comme (++", world") "hello" .

Pourquoi quelqu’un ferait ça? Pour les listes de fonctions, par exemple. Tous 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, ces dernières variantes seraient plus lisibles pour la plupart des gens.

… ou vous pourriez éviter le . et $ constructions en utilisant le pipelining :

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

C’est après avoir ajouté la fonction d’aide:

 (|>) xy = yx 

Un bon moyen d’en apprendre plus sur n’importe quoi (n’importe quelle fonction) est de vous rappeler que tout est une fonction! Ce mantra général aide, mais dans des cas spécifiques comme les opérateurs, il est utile de se souvenir de cette petite astuce:

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

et

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

Rappelez-vous juste d’utiliser :t liberally, et enveloppez vos opérateurs dans () !

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

  • ne pas utiliser si vous voulez passer le paramètre (appelez la fonction), et
  • n’utilisez pas $ s’il n’y a pas encore de paramètre (composez une fonction)

C’est

 show $ head [1, 2] 

mais jamais:

 show . head [1, 2] 

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

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

Ils ne sont pas du sucre syntaxique pour ne pas avoir besoin d’utiliser des parenthèses – ce sont des fonctions, – infixés, nous pouvons donc les appeler des opérateurs.

(.) est la fonction de composition. Alors

 result = (f . g) x 

est identique à la construction d’une fonction qui transmet le résultat de son argument passé à g on à f .

 h = \x -> f (gx) result = hx 

($) est une fonction d’application associative à droite avec une faible priorité de liaison. Donc, il ne fait que calculer les choses à sa droite en premier. Ainsi,

 result = f $ gx 

est la même chose que sur le plan de la procédure (ce qui compte puisque Haskell est évalué paresseusement, il commencera à évaluer d’abord):

 h = f g_x = gx result = h g_x 

ou plus concis:

 result = f (gx) 

Nous pouvons le voir en lisant la source de chaque fonction.

Lire la source

Voici la source de (.) :

 -- | 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 (.) fg = \x -> f (gx) 

Et voici la source de ($) :

 -- | Application operator. This operator is redundant, since ordinary -- application @(fx)@ 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 $ hx = f (g (hx)) -- -- 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 = fx 

Quand utiliser:

Utilisez la composition lorsque vous n’avez pas besoin d’évaluer immédiatement la fonction. Vous voulez peut-être passer la fonction résultant 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 sémantiquement préférable de faire

 f $ gx 

quand on a x (ou plutôt les arguments de g ), et fais:

 f . g 

quand on ne le fait pas

Je pense à un court exemple d’utilisation . et non $ aiderait à clarifier les choses.

 double x = x * 2 sortingple x = x * 3 times6 = double . sortingple :i times6 times6 :: Num c => c -> c 

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

Toutes les autres réponses sont plutôt 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 d’instatiarion avec des types de rang / quantifiés plus élevés. Si vous regardez le type de $ id par exemple, vous verrez que cela va prendre une fonction dont l’argument est lui-même une fonction polymorphe. De petites choses comme celle-là ne bénéficient pas de la même flexibilité avec un opérateur équivalent. (Cela me fait me demander si $! Mérite le même traitement ou non)