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



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

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

该函数有效,但是有没有办法将双映射(带有三个"$")重写为单个映射?

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有一个关于幺半群的部分,尽管在这种情况下,我不确定它是否增加了Data.Monoid文档之外的内容。

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

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]

作为最后一步,我们可以用 concatMap 替换concatmap的组成。

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

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

 outputList x y = concatMap (f -> f x y) [getRow,getColumn,getBlock]

正如它清楚地表达的那样,该函数的作用。但是,使用类型类抽象(如另一个答案所示)可能是一件好事,因为您可能会观察到您的问题具有一定的抽象结构并获得新的见解。

我会同意@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答案完全相同。哦,好吧!

最新更新