plusOne :: [[(Int, Int, Int)]] -> [[(Int, Int, Int)]]
给出一个三元组列表的列表。 如果我想遍历列表并+1到Int值,该如何处理?我不确定这是否是应该使用地图的情况。
有人可以指出我正确的方向吗?
答案 0 :(得分:7)
拆分功能。清单很简单;每个map
个。但是元组不会遍历它们的方式,例如Python,因此需要解压缩才能访问元素;通用编程可以做到这一点,而模式匹配则容易得多。元组可以容纳各种类型的字段,因此map
之类的内容无法访问所有字段。我们可以专门为三元组创建自己的地图类似物:
map3t :: (x -> y) -> (x, x, x) -> (y, y, y)
map3t f (a, b, c) = (f a, f b, f c)
现在我们可以检查类型的每个级别并分别处理:
op :: Int -> Int
op = (+1)
t3 :: (Int, Int, Int) -> (Int, Int, Int)
t3 = map3t op
lt3 :: [(Int, Int, Int)] -> [(Int, Int, Int)]
lt3 = map t3
llt3 :: [[(Int, Int, Int)]] -> [[(Int, Int, Int)]]
llt3 = map lt3
这不是递归,尽管map
可以使用递归来实现(Data.List.map
是)。每个函数为内部级别调用不同的函数。
答案 1 :(得分:1)
这是一个不太普通的方式示例,说明了如何使用map访问嵌套列表,也可以使用lambda函数在3tuples上将其匹配:
fun :: (Num a, Num b, Num c) => [[(a, b, c)]] -> [[(a, b, c)]]
fun xs = map (map(\(x,y,z) -> (x+1,y+1,z+1))) xs
优点:轻松易懂的oneliner解决特定问题
缺点:对于应用于元素的功能而言,它不是通用的,随着更复杂和更大的输入结构,可能变得模糊且无法控制。
使用硬编码功能进行映射,迫使您为每个操作制作一个新映射。因此,在这种情况下,更好的方法是重构函数本身,即:
fun2 f xs = map (map(op f)) xs
where
op f' (x,y,z) = (f' x,f' y, f' z)
使op
具有可以对特定类型进行操作的功能。
使函数的签名在操作类型上更为通用:(请注意,我们无法再确定之前为数字的x,y,z
类型(由于+1
操作)为我们提供了该函数的更通用版本,也使我们更加负责正确地匹配类型,没有对整数进行字符串操作等。)
fun2 :: (t -> c) -> [[(t, t, t)]] -> [[(c, c, c)]]
答案 2 :(得分:1)
定义一个合适的函子来包装您的元组。
data Three a = Three {getThree :: (a, a, a)} deriving (Show, Functor)
如果您不想使用DeriveFunctor
扩展名,则定义很简单:
instance Functor Three where
fmap f (Three (x, y, z)) = Three (f x, f y, f z)
然后,您可以简单地将plusOne
定义为
>>> plusOne = let f = getThree . fmap (+1) . Three in fmap (fmap f)
其中f
是一个包装三元组,将(+1)
映射到每个元素并解包结果的函数。这会映射到您的列表列表中:
> x = [[(1, 2, 3), (4,5,6)], [(7,8,9)]]
> plusOne x
[[(2,3,4),(5,6,7)],[(8,9,10)]]
您还可以使用Data.Functor.Compose
来消除fmap
的一个级别(或者,至少将其隐藏在另一组名称后面以打破单调):
> getCompose . fmap (getThree . fmap (+1) . Three) . Compose $ x
[[(2,3,4),(5,6,7)],[(8,9,10)]]
我们两次应用了相同的包装/ fmaping /展开方法。我们可以通过一个辅助函数将其抽象化
-- wrap, map, and unwrap
wmu pre post f = post . fmap f . pre
plusOne = wmu Compose getCompose $ wmu Three getThree $ (+1)
一个人可能会注意到wmu
和dimap
(专门用于(->)
)之间的相似性:
wmu pre post = dimap pre post . fmap
如果您首先可以将通用元组替换为自定义产品类型,那么一切就变得更加简单。
data Triplet a = Triplet a a a
-- Can be derived as well
instance Functor Triplet where
fmap f (Triplet x y z) = Triplet (f x) (f y) (f z)
plusOne :: [[Triplet Int]] -> [[Triplet Int]]
plusOne = fmap (fmap (fmap (+1)))