7

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

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

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

4

3 回答 3

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],并且任何东西的列表都是一个幺半群。Monoidally 组合列表与连接列表相同,因此我们可以替换concatmconcat.

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 有一个关于monoids 的部分,尽管在这种情况下,我不确定它是否会超出Data.Monoid 的文档

于 2012-09-27T10:46:36.857 回答
4

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

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

可以使用函数组合运算符(.)而不是函数应用运算符重写($),如下所示。

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

接下来我们注意到它mapfmapon 列表的另一个名称并且满足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]

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

于 2012-09-27T11:01:38.193 回答
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 . mapconcatMap

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

你完成了。

编辑: aaaaand 这与@Jan Christiansen 的回答完全相同。那好吧!

于 2012-09-27T11:07:38.837 回答