В Haskell я могу легко отобразить список:
map (\x -> 2*x) [1,2]
дает мне [2,4]. Есть ли какая-либо функция "mapTuple", которая будет работать так?
mapTuple (\x -> 2*x) (1,2)
с результатом (2,4).
В Haskell я могу легко отобразить список:
map (\x -> 2*x) [1,2]
дает мне [2,4]. Есть ли какая-либо функция "mapTuple", которая будет работать так?
mapTuple (\x -> 2*x) (1,2)
с результатом (2,4).
 Поиск в Hoogle не дает точных совпадений для (a -> b) -> (a, a) -> (b, b), который вам нужен, но это довольно легко сделать самому:
mapTuple :: (a -> b) -> (a, a) -> (b, b)
mapTuple f (a1, a2) = (f a1, f a2)
Обратите внимание: вам нужно будет определить новую функцию для 3-кортежей, 4-кортежей и т.д. - хотя такая потребность может быть признаком того, что вы не используете кортежи, как они были предназначены: в общем случае кортежи содержат значения разные типы, поэтому очень важно не применять одну функцию ко всем значениям.
Здесь довольно короткое решение без точек:
import Control.Monad (join)
import Control.Arrow ((***))
mapTuple = join (***)
		Вы можете использовать arrows из модуля Control.Arrow для создания функций, которые работают с кортежами.
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)
Затем ваш mapTuple становится
mapTuple f = f *** f
Если с вашим вопросом вы попросили функцию, которая отображает над кортежами произвольной arity, то я боюсь, что вы не можете, потому что они будут иметь разные типы (например, типы кортежей (a,b) и (a,b,c) совершенно разные и не связаны).
Вы можете использовать Bifunctor:
import Control.Monad  (join)
import Data.Bifunctor (bimap)
join bimap (2*) (1,2)
Это работает не только для пар, но и для ряда других типов, например. для Either.
 Bifunctor находится в base с версии 4.8. Ранее это было предоставлено пакетом bifunctors.
Вы также можете использовать lens для сопоставления кортежей:
import Control.Lens
mapPair = over both
Или вы можете сопоставить кортежи с до 10 элементами:
mapNtuple f = traverseOf each (return . f)
		Чтобы добавить другое решение к этому красочному набору... Вы также можете отобразить над произвольными n-кортежами, используя Общее программирование Scrap-Your-Boilerplate. Например:
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)
Обратите внимание, что явные аннотации типов важны, так как SYB выбирает поля по типу. Например, если один тип элемента tuple Float, он больше не будет удваиваться.
Да, для кортежей из 2 элементов вы можете использовать first и second, чтобы отобразить содержимое кортежа (не беспокойтесь о сигнатуре типа; a b c можно прочитать как b -> c в этой ситуации). Для больших кортежей вам следует использовать структуру данных и объективы.
Вот еще один способ:
mapPair :: (a -> b) -> (a, a) -> (b, b) -- this is the inferred type
mapPair f = uncurry ((,) `on` f)
Вам нужно Data.Function импортировать для функции on.
Вы также можете использовать Применения, которые имеют дополнительное преимущество, давая вам возможность применять различные функции для каждого элемента кортежа:
import Control.Applicative
mapTuple :: (a -> a') -> (b -> b') -> (a, b) -> (a', b')
mapTuple f g = (,) <$>  f . fst <*> g . snd
Встроенная версия:
(\f -> (,) <$>  f . fst <*> f . snd) (*2) (3, 4)
или с различными функциями карты и без лямбда:
(,) <$> (*2) . fst <*> (*7) . snd $ (3, 4)
Другая возможность - использовать стрелки:
import Control.Arrow
(+2) . fst &&& (+2) . snd $ (2, 3)
		Пакет extra предоставляет функцию both в Data.Tuple.Extra. Из документов:
Apply a single function to both components of a pair.
> both succ (1,2) == (2,3)
both :: (a -> b) -> (a, a) -> (b, b)
		Я только добавил пакет tuples-homogenous-h98 в Hackage, который решает эту проблему. Он добавляет обертки newtype для кортежей и определяет для них экземпляры Functor, Applicative, Foldable и Traversable. Используя пакет, вы можете делать такие вещи, как:
untuple2 . fmap (2 *) . Tuple2 $ (1, 2)
или zip кортежи вроде:
Tuple2 ((+ 1), (*2)) <*> Tuple2 (1, 10)
		Пакет uniplate предоставляет descend в модуле Data.Generics.Uniplate.Data. Эта функция будет применять функцию везде, где совпадают типы, поэтому ее можно применять к спискам, кортежам, любым или другим типам данных. Некоторые примеры:
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]
		Да, вы бы сделали:
map (\x -> (fst x *2, snd x *2)) [(1,2)]
 fst захватывает первый ввод данных в кортеже, а snd захватывает второй; поэтому строка кода говорит: "Возьмите кортеж и верните другой кортеж с первым и вторым элементами, двойным предыдущим".