Haskell:如何映射元组?

qua*_*dev 60 mapping haskell tuples

在Haskell中,我可以轻松地映射列表:

map (\x -> 2*x) [1,2]
Run Code Online (Sandbox Code Playgroud)

给了我[2,4].是否有任何"mapTuple"功能可以这样工作?

mapTuple (\x -> 2*x) (1,2)
Run Code Online (Sandbox Code Playgroud)

结果是(2,4).

Rot*_*sor 80

这是一个相当短的无点解决方案:

import Control.Monad (join)
import Control.Arrow ((***))

mapTuple = join (***)
Run Code Online (Sandbox Code Playgroud)

  • @Riccardo - `join`接受两个具有相同类型的参数的函数,`a-> a-> b`,并创建一个带有一个参数`a - > b`的新函数,将该参数传递给该参数的两个位置.原始功能.这是因为函数的Monad实例与`Reader` monad相同,给`join`类型`(a - > a - > b) - > a - > b`.当箭头没有写入中缀时,我发现它更容易解决,例如`join ::(a - >)((a - >)b) - >(a - >)b`. (13认同)

Bor*_*ris 44

在Hoogle搜索没有提供完全匹配(a -> b) -> (a, a) -> (b, b),这是您需要的类型,但您自己很容易做到:

mapTuple :: (a -> b) -> (a, a) -> (b, b)
mapTuple f (a1, a2) = (f a1, f a2)
Run Code Online (Sandbox Code Playgroud)

注意,你必须为3元组,4元组等定义一个新函数 - 虽然这样的需要可能是一个标志,你没有像他们想要的那样使用元组:通常,元组保存不同类型的值,所以想要将单个函数应用于所有值并不常见.

  • `Control.Arrow`*在标准库中是*,而`Control.Bifunctor`不是太远...... (8认同)
  • 您总是可以使用Applicative技巧在函数中提供两次参数:`(bimap <$> id <*> id)(*2)(3,5)` (3认同)

Lan*_*dei 25

你可以使用Bifunctor:

import Control.Monad  (join)
import Data.Bifunctor (bimap)

join bimap (2*) (1,2)
Run Code Online (Sandbox Code Playgroud)

这不仅适用于对,也适用于许多其他类型,例如Either.

Bifunctor在4.8版本的基础上.以前它是由bifunctors包提供的.


Ric*_* T. 23

您可以使用模块中的箭头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)
Run Code Online (Sandbox Code Playgroud)

然后你的mapTuple就变成了

mapTuple f = f *** f
Run Code Online (Sandbox Code Playgroud)

如果你的问题是你要求一个映射了任意arity的元组的函数,那么我担心你不能因为它们会有不同的类型(例如元组类型(a,b)并且(a,b,c)完全不同且不相关).


Seb*_*ner 20

您还可以使用镜头来映射元组:

import Control.Lens
mapPair = over both
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用最多10个元素映射元组:

mapNtuple f = traverseOf each (return . f)
Run Code Online (Sandbox Code Playgroud)

  • `over each`似乎也有效:`(每个)(+ 1)(1,2,3,4,5,6,7,8,9)==(2,3,4,5,6, 7,8,9,10)` (4认同)

Pet*_*ann 12

要为这个彩色集添加另一个解决方案......您还可以使用Scrap-Your-Boilerplate泛型编程映射任意n元组.例如:

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)
Run Code Online (Sandbox Code Playgroud)

请注意,显式类型注释很重要,因为SYB按类型选择字段.Float例如,如果一个元组构成一个元组元素类型,它就不会再加倍.

  • 我可能错了,但鉴于GHC的[`Data.Data`文档](http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data shells.html)引用了SYB,我打赌它是一个直接的后代. (3认同)

ciu*_*can 12

这是另一种方式:

mapPair :: (a -> b) -> (a, a) -> (b, b) -- this is the inferred type
mapPair f = uncurry ((,) `on` f)
Run Code Online (Sandbox Code Playgroud)

您需要Data.Function导入on功能.

  • 我认为这是最简单、最优雅的解决方案。+1 (2认同)

dfl*_*str 9

是的,对于2个元组的元组,您可以使用firstsecond映射元组的内容(不要担心类型签名; a b c可以b -> c在这种情况下读取).对于较大的元组,您应该考虑使用数据结构和镜头.


Nei*_*ell 7

额外的软件包提供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)
Run Code Online (Sandbox Code Playgroud)


Pio*_*lka 6

您还可以使用Applicatives,它具有额外的好处,使您可以为每个元组元素应用不同的函数:

import Control.Applicative

mapTuple :: (a -> a') -> (b -> b') -> (a, b) -> (a', b')
mapTuple f g = (,) <$>  f . fst <*> g . snd
Run Code Online (Sandbox Code Playgroud)

内联版本:

(\f -> (,) <$>  f . fst <*> f . snd) (*2) (3, 4)
Run Code Online (Sandbox Code Playgroud)

或者使用不同的map函数和没有lambda:

(,) <$> (*2) . fst <*> (*7) . snd $ (3, 4)
Run Code Online (Sandbox Code Playgroud)

其他可能性是使用箭头:

import Control.Arrow

(+2) . fst &&& (+2) . snd $ (2, 3)
Run Code Online (Sandbox Code Playgroud)


Pet*_*lák 5

我刚刚向 Hackage 添加了一个包tuples-homous-h98来解决这个问题。它为元组添加newtype包装器并为它们定义FunctorApplicativeFoldable实例。Traversable使用该包您可以执行以下操作:

untuple2 . fmap (2 *) . Tuple2 $ (1, 2)
Run Code Online (Sandbox Code Playgroud)

或压缩元组,例如:

Tuple2 ((+ 1), (*2)) <*> Tuple2 (1, 10)
Run Code Online (Sandbox Code Playgroud)