我有三个带有两个参数(x 和 y)的函数(getRow
, getColumn
, getBlock
),每个函数都生成一个相同类型的列表。我想编写第四个函数来连接它们的输出:
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]
,并且任何东西的列表都是一个幺半群。Monoidally 组合列表与连接列表相同,因此我们可以替换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 有一个关于monoids 的部分,尽管在这种情况下,我不确定它是否会超出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
on 列表的另一个名称并且满足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]
正如它清楚地表达的那样,该功能的作用。但是,使用类型类抽象(如另一个答案所示)可能是一件好事,因为您可能会观察到您的问题具有一定的抽象结构并获得新的见解。
我会选择@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]
你完成了。
编辑: aaaaand 这与@Jan Christiansen 的回答完全相同。那好吧!