À Haskell, je peux facilement mapper une liste:
map (\x -> 2*x) [1,2]
donne moi [2,4]
. Y a-t-il une fonction "mapTuple" qui fonctionnerait comme ça?
mapTuple (\x -> 2*x) (1,2)
le résultat étant (2,4)
.
Recherche sur Hoogle ne donne aucune correspondance exacte pour (a -> b) -> (a, a) -> (b, b)
, qui est le type dont vous avez besoin, mais il est assez facile de le faire vous-même:
mapTuple :: (a -> b) -> (a, a) -> (b, b)
mapTuple f (a1, a2) = (f a1, f a2)
Remarque, vous devrez définir une nouvelle fonction pour les 3-tuples, 4-tuples, etc. il n'est donc pas très courant de vouloir appliquer une seule fonction à toutes les valeurs.
Voici une solution sans point assez courte:
import Control.Monad (join)
import Control.Arrow ((***))
mapTuple = join (***)
Vous pouvez utiliser Bifunctor
:
import Control.Monad (join)
import Data.Bifunctor (bimap)
join bimap (2*) (1,2)
Cela fonctionne non seulement pour les paires, mais aussi pour un certain nombre d'autres types, par exemple pour Either
.
Bifunctor
est dans base à partir de la version 4.8. Auparavant, il était fourni par le paquet bifunctors .
Vous pouvez utiliser flèches à partir du module Control.Arrow
pour composer des fonctions qui fonctionnent sur des tuples.
Prelude Control.Arrow> let f = (*2) *** (*2)
Prelude Control.Arrow> f (1,2)
(2,4)
Prelude Control.Arrow> let f' = (*2) *** (*3)
Prelude Control.Arrow> f (2,2)
(4,4)
Prelude Control.Arrow> f' (2,2)
(4,6)
Votre mapTuple devient alors
mapTuple f = f *** f
Si, avec votre question, vous avez demandé une fonction qui mappe sur des tuples d'arité arbitraire, alors je crains que vous ne puissiez pas, car ils auraient des types différents (par exemple, les types de tuple (a,b)
et (a,b,c)
sont totalement différents et indépendants).
Vous pouvez également utiliser lens pour mapper les tuples:
import Control.Lens
mapPair = over both
Ou vous pouvez mapper des tuples avec jusqu'à 10 éléments:
mapNtuple f = traverseOf each (return . f)
Pour ajouter une autre solution à cet ensemble coloré ... Vous pouvez également cartographier des n-tuples arbitraires en utilisant programmation générique Scrap-Your-Boilerplate . Par exemple:
import Data.Data
import Data.Generics.Aliases
double :: Int -> Int
double = (*2)
Tuple :: (Int, Int, Int, Int)
Tuple = gmapT (mkT double) (1,2,3,4)
Notez que les annotations de type explicites sont importantes, car SYB sélectionne les champs par type. Si on fait un type d'élément Tuple Float
, par exemple, il ne serait plus doublé.
Voici une autre façon:
mapPair :: (a -> b) -> (a, a) -> (b, b) -- this is the inferred type
mapPair f = uncurry ((,) `on` f)
Vous avez besoin Data.Function
importé pour la fonction on
.
Oui, pour des tuples de 2 éléments, vous pouvez utiliser first
et second
pour mapper le contenu d'un tuple (ne vous inquiétez pas à propos de la signature de type; a b c
peut être lu comme b -> c
dans cette situation). Pour les tuples plus gros, vous devriez plutôt envisager d'utiliser une structure de données et des lentilles.
Le package extra fournit la fonction both
dans le module Data.Tuple.Extra . De la documentation:
Apply a single function to both components of a pair.
> both succ (1,2) == (2,3)
both :: (a -> b) -> (a, a) -> (b, b)
Vous pouvez également utiliser des Applicatifs qui ont l'avantage supplémentaire de vous donner la possibilité d'appliquer différentes fonctions pour chaque élément Tuple:
import Control.Applicative
mapTuple :: (a -> a') -> (b -> b') -> (a, b) -> (a', b')
mapTuple f g = (,) <$> f . fst <*> g . snd
Version en ligne:
(\f -> (,) <$> f . fst <*> f . snd) (*2) (3, 4)
ou avec différentes fonctions cartographiques et sans lambda:
(,) <$> (*2) . fst <*> (*7) . snd $ (3, 4)
Une autre possibilité serait d'utiliser des flèches:
import Control.Arrow
(+2) . fst &&& (+2) . snd $ (2, 3)
Le package niplate fournit la fonction descend dans le module Data.Generics.Uniplate.Data . Cette fonction appliquera la fonction partout où les types correspondent, elle peut donc être appliquée aux listes, aux tuples, à l'un ou à la plupart des autres types de données. Quelques exemples:
descend (\x -> 2*x) (1,2) == (2,4)
descend (\x -> 2*x) (1,"test",Just 2) == (2,"test",Just 4)
descend (\x -> 2*x) (1,2,3,4,5) == (2,4,6,8,10)
descend (\x -> 2*x) [1,2,3,4,5] == [2,4,6,8,10]
Je viens d'ajouter un package tuples-homogenous-h98 à Hackage qui résout ce problème. Il ajoute des enveloppes newtype
pour les tuples et définit pour eux des instances Functor
, Applicative
, Foldable
et Traversable
. En utilisant le package, vous pouvez faire des choses comme:
untuple2 . fmap (2 *) . Tuple2 $ (1, 2)
ou Zip tuples comme:
Tuple2 ((+ 1), (*2)) <*> Tuple2 (1, 10)
Oui, vous feriez:
map (\x -> (fst x *2, snd x *2)) [(1,2)]
fst
récupère la première entrée de données dans un Tuple et snd
récupère la seconde; ainsi, la ligne de code dit "prenez un tuple et renvoyez un autre tuple avec les premier et deuxième éléments doublant le précédent".