通过 pointfree 中的地图乘法表 Haskell

Multiplication tables via maps in pointfree Haskell

在 Haskell 中,我定义了一个乘法-table 生成器函数,如下所示:

multTable :: [[Int]]
multTable=map (\b -> map (b*) [1..12]) [1..12]

哪个输出...

[[1,2,3,4,5,6,7,8,9,10,11,12],
[2,4,6,8,10,12,14,16,18,20,22,24],
...[12,24,36,48,60,72,84,96,108,120,132,144]]

...如果您眯着眼睛,这看起来像是 1..12 的乘法 tables。到目前为止这么好......现在我试图让它变得无意义,认为它应该很容易组合。所以我正在采取一些小步骤,我相信我已经走到了一半,因为我可以做到这一点:

map ($ 4) $ map (*) [1..12]

...这给了我:

[4,8,12,16,20,24,28,32,36,40,44,48]

看看这个,第二张地图给出了 [a->a],这里具体是一个将值 1 到 12 乘以某个数字的函数列表。第一个映射评估这些函数中的每一个,为它们提供值 4,进而生成 [4,8..] 行。

我并不一定期望 pointfree 版本更短、更简洁甚至更具可读性。作为一个 Haskell 新手,我只是想了解它是如何实现的。

有一个工具叫做 pointfree on hackage, which helps you tremendously (see also the wiki)。它创建以下版本:

multTable = map (flip map [1..12] . (*)) [1..12]

我们如何到达那里?好吧,让我们稍微移动一下运算符并尝试在您的函数上应用 flip 直到我们得到类似的结果:

multTable = map (\b -> map (b*) [1..12])           [1..12] 
          = map (\b -> map ((*) b) [1..12])        [1..12] -- change to prefix notation
          = map (\b -> flip map [1..12] ((*) b)    [1..12] -- flip map
          = map (\b -> flip map [1..12] . (*) $ b) [1..12] -- associativity
          = map (flip map [1..12] . (*))           [1..12] -- eta-reduction

注意我们只需要关注\b -> map (b*) [1..12].

也就是说,pointfree 版本感觉有点做作。毕竟,你想要一个 table,然后 来创建一个:list comprehensions:

multTable = [[x * y | x <- [1..12]] | y <- [1..12]]

这个版本更容易阅读,对 reader 来说更明显,但话又说回来,您可能认为会发生这种情况:

I'm not necessarily expecting the pointfree version to be shorter, more concise or even more readable.

在对 Zeta 的回答的评论中,他建议重新实现您的代码:

do
    x <- [1..12]
    return $ do
        y <- [1..12]
        return (x * y)

它有一种漂亮的模式,并以这种方式脱糖:

[1..12] >>= \x ->
return $ [1..12] >>= \y ->
         return (x * y)

现在突然想到m >>= \x -> return (f x)fmap f m是一样的,所以上面的代码真的只需要Functor而不是Monad。所以这让我想知道“do-函子符号”可能是什么样子,以及它如何脱糖。定义 for = flip fmap,这是我认为它可能看起来脱糖的方式:

for [1..12] $ \x ->
for [1..12] $ \y ->
x*y

(希望你能看到与脱糖的 do 符号的相似之处。)无论如何,pointfree-ifying 这给出了一些非常漂亮的东西:

for [1..12] $ for [1..12] . (*)