【问题标题】:"Double" function composition in HaskellHaskell 中的“双重”函数组合
【发布时间】:2014-02-28 19:18:11
【问题描述】:

我从编译器的建议中发现了一些对我来说似乎很奇怪的东西。

我做了一个数据类型来表示二进制数如下:

data Bin = Zero | One

我选择将多位二进制数表示为 Bin 类型的列表,如下所示:

myNum :: [Bin]
myNum = [One, Zero, One, One] -- represents the number 1011

当然,我想以更方便的方式显示我的二进制数,所以我创建了一个 Show 实例来为我做这件事:

instance Show Bin where
    show Zero = "0"
    show One  = "1"
    showList []     = showString ""
    showList (x:xs) = showString (show x) . showList xs

这有效,print myNum 正确显示 1011

我还是 Haskell 的新手,这是我第一次使用 showList。但这对我来说很有意义。由于 showList 的类型为 [a] -> ShowS(它本身是 [a] -> (String -> String) 的别名),我知道我通过使用函数组合来“连接”列表的元素。但是编译器建议我把showList函数重新定义一下:

Warning: Use foldr
  Found:
    showList [] = showString ""
    showList (x : xs) = showString (show x) . showList xs
  Why not:
    showList xs = foldr ((.) . showString . show) (showString "") xs

而且,一旦我替换了它的建议,它就提出了进一步的建议:

Error: Eta reduce
  Found:
    showList xs = foldr ((.) . showString . show) (showString "") xs
  Why not:
    showList = foldr ((.) . showString . show) (showString "")

我了解 Eta reduce 错误,因为通常最好编写无点函数。但我在第一次转换时遇到了麻烦。我看到foldr 的第二个参数是“基本情况”正确身份,但我很难理解foldr 的第一个参数中发生了什么。所以我有两个问题:

  1. 如何重写((.) . showString . show)?比如我知道(f . g) x可以改写成f (g x)
  2. 这在foldr 的上下文中实际上做了什么?

【问题讨论】:

  • 您可能还想将showString . show 简化为shows,这应该总是做同样的事情,但可能更有效。事实上,您可以将所有这些简化为showList xs s = foldr shows s xs,甚至是showList = flip foldr shows

标签: haskell functional-programming abstract-data-type


【解决方案1】:

改写成这样怎么样,先把组合的模式分解成一个单独的函数

 compose :: [a -> a] -> a -> a
 compose = foldr (.) id

您可以将其想象为获取列表 f : g : h : [] 并将 [] 替换为 id:.,留给你 f . g . h . idf . g . h

接下来让我们用它来重写你的例子,让它更易读

  instance Show Bin where
    showList = compose . map (showString . show)

现在它比你所拥有的更清晰一些,尽管它在功能上是相同的。事实上,这甚至可能最终编译为相同的东西,因为 GHC 可能会融合它(?)。我们将每个项目转换为String -> StringShowS,然后我们将它们全部组合起来。为compose 提供名称和类型可以更轻松地查看正在发生的事情。

我想这也可以写

 showList = appEndo . mconcat . map (Endo . showString . show)

这与上面的相同,但依赖于a -> a 形成一个幺半群,mconcat 概括了将一组幺半群组合为一个的想法。我们需要 Endo 位,因为 monoid 实例实际上是为

定义的
newtype Endo a = End {appEndo :: a -> a}

【讨论】:

  • 这很有帮助!我喜欢你分手的方式。我最终只是在 showList 的 where 子句中定义了 compose 并使用了您的示例。更清晰!
猜你喜欢
  • 2019-08-17
  • 1970-01-01
  • 2016-04-30
  • 2010-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多