Trick pour les arguments « réutilisation » dans Haskell?
-
30-09-2019 - |
Question
De temps en temps, je trébuche sur le problème que je veux exprimer « s'il vous plaît utiliser le dernier argument deux fois », par exemple afin d'écrire le style Pointfree ou pour éviter un lambda. Par exemple.
sqr x = x * x
pourrait être écrit comme
sqr = doubleArgs (*) where
doubleArgs f x = f x x
Ou considérer cette fonction un peu plus compliquée (prise cette question ):
ins x xs = zipWith (\ a b -> a ++ (x:b)) (inits xs) (tails xs)
Je pourrais écrire ce code Pointfree s'il y avait une fonction comme ceci:
ins x = dup (zipWith (\ a b -> a ++ (x:b))) inits tails where
dup f f1 f2 x = f (f1 x) (f2 x)
Mais comme je ne peux pas trouver quelque chose comme doubleArgs ou dup dans Hoogle, donc je suppose que je pourrais manquer un truc ou idiome ici.
La solution
De Control.Monad
:
join :: (Monad m) -> m (m a) -> m a
join m = m >>= id
instance Monad ((->) r) where
return = const
m >>= f = \x -> f (m x) x
Expansion:
join :: (a -> a -> b) -> (a -> b)
join f = f >>= id
= \x -> id (f x) x
= \x -> f x x
Alors, oui, Control.Monad.join
.
Oh, et pour votre exemple Pointfree, avez-vous essayé d'utiliser la notation applicative (de Control.Applicative
):
ins x = zipWith (\a b -> a ++ (x:b)) <$> inits <*> tails
(je aussi ne sais pas pourquoi les gens sont si friands de a ++ (x:b)
au lieu de a ++ [x] ++ b
... ce n'est pas plus rapide - le revêtement intérieur prendra soin - et celui-ci est bien plus symétrique Eh bien!)
Autres conseils
Ce que vous appelez « doubleArgs » est plus souvent appelé dup - il est le W combinateur (appelé dans la paruline Pour Mock a Mockingbird) - « le duplicateur élémentaire ».
Ce que vous appelez 'dup' est en fait le 'Starling prime' Combinator.
Haskell a une assez petite "base Combinator" voir Data.Function, ainsi que quelques opérations Applicative et monadiques ajouter plus combinators "standard" en vertu des instances de fonction pour Applicative et Monad (<*> de Applicative est le S - Starling Combinator pour l'instance fonctionnelle, liftA2 & liftM2 sont Starling-prime). Il ne semble pas être beaucoup d'enthousiasme dans la communauté pour l'expansion Data.Function, donc tout combinateurs sont amusants, pragmatiquement Je suis venu à préférer longue main dans les situations où un combinateur est pas directement disponible.
Voici une autre solution pour la deuxième partie de ma question: Flèches
import Control.Arrow
ins x = inits &&& tails >>> second (map (x:)) >>> uncurry (zipWith (++))
Le &&&
( « sortance ») distribue un argument à deux fonctions et renvoie la paire des résultats. >>>
( « et ») inverse l'ordre d'application de fonction, ce qui permet d'avoir une chaîne d'opérations de gauche à droite. second
ne fonctionne que sur la deuxième partie d'une paire. Bien sûr, vous avez besoin d'un uncurry
à la fin pour alimenter la paire dans une fonction attendant deux arguments.