【问题标题】:Multiplication tables via maps in pointfree Haskell通过无点Haskell中的映射乘法表
【发布时间】:2015-03-02 23:21:36
【问题描述】:

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

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 的乘法表。到目前为止一切顺利......现在我试图让它变得无意义,认为它应该很容易组合。所以我正在迈出一小步,我相信我已经成功了,因为我可以做到这一点:

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

...这给了我:

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

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

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

【问题讨论】:

  • Haskell 新手似乎爱上了无点风格。爱情是盲目的;你会从中成长。顺便说一句,这是一个不错的(不是无意义的)版本:[[x * y | x <- [1..12]] | y <- [1..12]]
  • @BenjaminHodgson 如果他玩得开心,为什么不让他玩呢?看看你能把一些编程技术或概念带到多远很有趣。最终是否实用,好吧,这并不重要。
  • Zeta 的回答在逐步改进中具有指导意义,这些改进从我的原始形式开始,以无点形式结束。丹尼尔的回答有一种吸引人的风格,我觉得非常直观。两个答案都很棒。谢谢。

标签: haskell pointfree


【解决方案1】:

在 hackage 上有一个名为 pointfree 的工具,它可以极大地帮助您(另请参阅 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 版本感觉有点做作。毕竟,您需要一张表格,然后 Benjamin's shows a rather nice way 创建一个:列表推导:

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

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

我不一定期望无点版本更短、更简洁甚至更具可读性。

【讨论】:

  • do 符号变体不如列表理解:do {x &lt;- [1..12]; return $ do {y &lt;- [1..12]; return $ x * y}}。但是,我已经厌倦了在这个答案中以一种很好的方式包含它,我可能会在明天添加它。
  • “函子理解”版本特别好:let for = flip fmap in for [1..12] $ \x -&gt; for [1..12] $ \y -&gt; x*y。如果你对 that 进行无指向化,你会得到for [1..12] $ for [1..12] . (*),这对我来说看起来很漂亮。
  • @DanielWagner:你想添加这个作为答案吗?它看起来真的很漂亮。
【解决方案2】:

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

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

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

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

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

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

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

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

【讨论】:

  • 我不小心省略了 fmap 中的“f”,它仍然适用于您提供的其余表达式。 flipmap(或 fmap)作为 forflip 对于来自命令式背景的我来说很有意义。这是一个非常有趣的见解。
  • @devgeezer 是的,对于列表,fmap = map。很高兴你喜欢它。
猜你喜欢
  • 2011-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-13
  • 1970-01-01
  • 2022-01-14
  • 1970-01-01
相关资源
最近更新 更多