如何在Haskell中映射多个参数的函数列表?

时间:2012-09-27 10:26:16

标签: haskell currying

我有三个函数(getRowgetColumngetBlock),它们有两个参数(x和y),每个参数都生成一个相同类型的列表。我想写第四个连接输出的函数:

outputList :: Int -> Int -> [Maybe Int]
outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock]

该功能有效,但有没有办法将双地图(三个'$')重写为一张地图?

3 个答案:

答案 0 :(得分:23)

import Data.Monoid

outputList :: Int -> Int -> [Maybe Int]
outputList = mconcat [getRow, getColumn, getBlock]

你应该得到解释。

首先,我将明确指出所有这些函数都具有相同的类型。

outputList, getRow, getColumn, getBlock :: Int -> Int -> [Maybe Int]

现在让我们从你的原始定义开始吧。

outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock]

这些函数产生[Maybe Int],任何事物的列表都是幺半群。单一组合列表与连接列表相同,因此我们可以将concat替换为mconcat

outputList x y = mconcat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock]

另一个幺半群是一个函数,如果它的结果​​是一个幺半群。也就是说,如果b是幺半群,那么a -> b也是幺半群。单调组合函数与使用相同参数调用函数相同,然后单独组合结果。

所以我们可以简化为

outputList x = mconcat $ map ($ x) [getRow,getColumn,getBlock]

然后再次

outputList = mconcat [getRow,getColumn,getBlock]

我们已经完成了!


Typeclassopedia has a section about monoids,虽然在这种情况下我不确定它是否会超出documentation for Data.Monoid

答案 1 :(得分:4)

作为第一步,我们观察到您的定义

outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock]
可以使用函数组合运算符(.)而不是函数应用程序运算符($)重写

,如下所示。

outputList x y = (concat . map ($ y) . map ($ x)) [getRow,getColumn,getBlock]

接下来,我们注意到map是列表中fmap的另一个名称,并且符合fmap法律,因此,我们特别注意map (f . g) == map f . map g。我们应用此法律来定义使用map的单个应用程序的版本。

outputList x y = (concat . map (($ y) . ($ x))) [getRow,getColumn,getBlock]

作为最后一步,我们可以用concat替换mapconcatMap的合成。

outputList x y = concatMap (($ y) . ($ x)) [getRow,getColumn,getBlock]

最后,在我看来,尽管Haskell程序员倾向于使用许多花哨的运算符,但通过

来定义函数并不是一种耻辱。
 outputList x y = concatMap (\f -> f x y) [getRow,getColumn,getBlock]

因为它清楚地表达了,该功能的作用。但是,使用类型类抽象(如在另一个答案中所示)可能是一件好事,因为您可能会发现您的问题具有某种抽象结构并获得新的见解。

答案 2 :(得分:2)

我会选择@ dave4420的答案,因为它最简洁,并且完全表达了你的意思。但是,如果您不想依赖Data.Monoid,那么您可以按如下方式重写

原始代码:

outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock]

融合两张地图:

outputList x y = concat . map (($y) . ($x)) [getRow,getColumn,getBlock]

concat . map替换为concatMap

outputList x y = concatMap (($y) . ($x)) [getRow,getColumn,getBlock]

你已经完成了。

编辑: aaaa这与@Jan Christiansen的答案完全相同。哦,好吧!