25

我编写了以下代码,它采用了一堆点并使用光泽库将它们绘制在屏幕上。

let s = blocks pes
    pts = map (map mkPt) s  {- stitches to points-}
    lines = map Line pts    {-points to lines -}
    pict = Pictures lines   {- lines to a picture -}
  in do  displayInWindow "My Window" (200, 200) (10, 10) white pict

它工作得很好,但我突然想到有一个重复的模式:一系列函数调用,每个函数调用的结果都输入下一个参数的最后一个参数。因此,我通过删除中间变量、颠倒顺序并使用函数组合(“。”)链接函数进行重构,如下所示:

let pict = Pictures . (map Line) . (map $ map $ mkPt) . blocks $ pes
                in do  displayInWindow "My Window" (200, 200) (10, 10) white pict

令人高兴的是,这也很好用。但我想知道我是否在强调可读性,或者我只是不习惯阅读和编写无点样式代码。另外,我如何推断这段代码?第二个版本更高效,还是更简洁?我可以在风格上做些什么来使它更清晰吗?

4

2 回答 2

31

一些快速的建议:

let pict = Pictures . (map Line) . (map $ map $ mkPt) . blocks $ pes
in do displayInWindow "My Window" (200, 200) (10, 10) white pict

你有一些多余的东西可以直接删除:

let pict = Pictures . map Line . (map $ map mkPt) . blocks $ pes
in do displayInWindow "My Window" (200, 200) (10, 10) white pict

无论如何,您都不会避免使用该map (map mkPt)术语的括号,因此请摆脱$

let pict = Pictures . map Line . map (map mkPt) . blocks $ pes
in do displayInWindow "My Window" (200, 200) (10, 10) white pict

为了清楚起见,您可以在多行上编写组合链:

let pict = Pictures 
         . map Line 
         . map (map mkPt) 
         . blocks $ pes
in do displayInWindow "My Window" (200, 200) (10, 10) white pict

do块是多余的,因为它只有一个语句,您可以将最终应用程序移到定义之外:

let displayPict = displayInWindow "My Window" (200, 200) (10, 10) white
                . Pictures 
                . map Line 
                . map (map mkPt) 
                . blocks
in displayPict pes

您可以合并两个maps:

let displayPict = displayInWindow "My Window" (200, 200) (10, 10) white
                . Pictures 
                . map (Line . map mkPt) 
                . blocks
in displayPict pes

有时,对于长链来说,使用反向组合运算符 from 也更具可读性Control.Arrow

let displayPict = blocks
                  >>> map (Line . map mkPt) 
                  >>> Pictures
                  >>> displayInWindow "My Window" (200, 200) (10, 10) white
in displayPict pes

但所有这些都是可选的;给你的代码调味。

关于效率,我认为没有理由认为这两者会有所不同,一旦 GHC 的优化器完成了代码。

于 2011-06-27T14:52:57.090 回答
4

无点风格可以很好地非常清楚地表明一个函数只是输入的一系列转换。阅读以下内容真的很容易:

foo = map show . doThis . doThat . etc

因为它看起来像典型的无点代码,所以熟悉它的人将能够准确地看到什么是重要的,没有噪音。将此与以下内容进行比较:

foo x = d
    where
        a = etc x
        c = doThis b
        b = doThat a
        d = map show c

显然,这有点做作,但想法是需要阅读并密切注意额外的定义,以便了解foo真正在做什么。a用作多个辅助函数的参数?怎么样b?数据是否以出乎意料的方式流经这些辅助函数?在这种情况下,无点风格是说“事情只是在管道中被转换,你不需要考虑奇怪的事情”

在其他情况下,无点样式确实可以混淆事物。通常那是你会打破 let 和 wheres 的时候。所以,简洁并不是真正的唯一目标,减少心理负担也很重要。(正如其他人评论的那样,无点样式在优化代码中不应该有性能差异)。

于 2011-06-29T17:49:25.760 回答