我有三个函数(getRow
,getColumn
,getBlock
),它们有两个参数(x和y),每个参数都生成一个相同类型的列表。我想写第四个连接输出的函数:
outputList :: Int -> Int -> [Maybe Int]
outputList x y = concat . map ($ y) $ map ($ x) [getRow,getColumn,getBlock]
该功能有效,但有没有办法将双地图(三个'$')重写为一张地图?
答案 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
替换map
和concatMap
的合成。
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的答案完全相同。哦,好吧!