正确评论函数式编程

Cam*_*Cam 12 scheme comments functional-programming coding-style readability

我一直在学习计划,我才意识到我真的不知道如何正确评论我的功能方案代码.我知道当然如何添加评论 - 你添加一个;并在其后添加评论.我的问题是应该在评论中加入什么,我应该在哪里评论其他程序员阅读我的代码的最大可读性和可理解性?

这是我写的代码片段.这是一个叫做的函数display-n.可以使用任意数量的参数调用它,并按照提供的顺序将每个参数输出到屏幕.

(define display-n
  (lambda nums
    (letrec ((display-n-inner 
              (lambda (nums)
                (display (car nums))
                (if (not (equal? (cdr nums) (quote ()))
                    (display-n-inner (cdr nums))))))
      (display-n-inner nums))))
Run Code Online (Sandbox Code Playgroud)

编辑:改进黏合,取而代之'()(quote ())避免SO搞乱格式.

我只是不确定如何/在哪里添加评论以使其更容易理解.我见过的一些方案代码只是在顶部有注释,如果你想使用代码,这很好,但如果你想理解/修改它就没有用.

另外 - 我应该如何评论宏?

Nie*_*jou 6

Lisp评论的共同风格是

  • 四个分号用于评论文件的整个子部分.
  • 用于引入单个程序的三个分号.
  • 两行用于描述以下行中的表达式/过程定义.
  • 一个分号用于结束评论.

程序概述注释应该遵循RnRS文档的样式,所以只是按原样添加注释,看起来像

;;; Procedure: display-n NUM ...
;; Output each argument to the screen in the order they are provided.
(define 
  display-n (lambda nums
              (letrec ((display-n-inner (lambda (nums)
                                          (display (car nums))
                                          (if (not (equal? (cdr nums) '()))
                                              (display-n-inner (cdr nums))))))
                (display-n-inner nums))))

注意我不会在整个过程描述中使用三个分号,因为它会搞砸Emacs中的fill-paragraph.


现在关于代码,我会抛弃整个define-variable-as-a-lambda的东西.是的,我认为这是定义一个函数的"最纯粹的"方式,并且它与定义过程的良好一致性是LET和其他过程的结果,但是有一个合成糖的原因,它是为了让事情变得更多可读.对于LETREC来说也是如此 - 只需使用内部DEFINE,这是相同的,但更具可读性.

DISPLAY-N-INNER的参数被称为NUMS并不是什么大不了的事,因为程序如此之短且DISPLAY-N无论如何都直接将其NUMS直接交给它.不过,"DISPLAY-N-INNER"是一个蹩脚的名字.你会给它一些具有更多语义含义的东西,或者给它一个简单的名字,如"ITER"或"LOOP".

现在讲述程序的逻辑.首先,(equal? (cdr nums) '())是愚蠢的,而且更好(null? (cdr nums)).实际上,当您在整个列表上操作时,最好使基本案例测试列表本身而不是其CDR是否为空.这样,如果你没有传递任何参数,程序就不会出错(除非你希望它这样做,但我认为如果没有任何东西,DISPLAY-N 什么也不是更有意义的).此外,您应该测试是否停止该过程,而不是是否继续:

(define (display-n . nums)
  (define (iter nums)
    (if (null? nums)
        #t  ; It doesn't matter what it returns.
        (begin (display (car nums))
               (iter (cdr nums)))))
  (iter nums))

但是对于所有这些,我会说程序本身并不是完成它所做任务的最佳方法,因为它过于关注遍历列表的细节.相反,你会使用更抽象的FOR-EACH方法来完成工作.

(define (display-n . nums)
  (for-each display nums))

通过这种方式,他可以理解FOR-EACH将显示NUMS的每个元素,而不是陷入CAR和CDR细节的过程的读者.


Eli*_*lay 5

一些随机笔记:

  • 传统上,Scheme 和 Lisp 代码用于;;;顶级注释、;;代码中的注释以及;与它们所注释的代码位于同一行的注释。Emacs 对此提供支持,并以不同的方式对待每一个。但特别是在计划方面,这不再像以前那样流行,但;;和之间的差异;仍然很常见。

  • 大多数现代方案都采用了新的注释:有:

    • #|...|#用于块注释——对于对整个文件进行注释的长文本段很有用。
    • #;<expr>是一个注释,使实现忽略表达式,这对于调试很有用。
  • 至于要编写的实际内容,这与任何其他语言没有什么不同,除了使用更实用的方法时,您通常在如何布局代码方面有更多选择。它还使得编写组合成较大功能块的较小函数变得更加方便——这也改变了文档风格,因为许多这样的小函数将是“自文档化”的(因为它们易于阅读并且非常有用)他们的工作方式显而易见)。

  • 我讨厌听起来像破唱片,但我仍然认为你应该花一些时间在 HtDP 上。它在设计方案中鼓励的一件事是首先编写示例,然后编写文档,然后将其扩展为实际代码。此外,这个秘籍还为您提供了具有非常标准的注释集的代码:输入/输出类型、目的声明、一些有关如何在必要时实现该函数的文档,以及可以将示例视为另一种文档(这将变成“真实”代码中的注释代码)。(还有其他书籍对文档采取类似的立场。)

  • 最后,记录宏与记录任何其他代码没有什么不同。唯一可能非常不同的是注释中所写的内容:您倾向于描述它扩展的代码,而不是描述某个函数正在做什么,因此注释也更多地在元级别上。宏的常见方法是在宏内部进行最少的工作——只做该级别所需的工作(例如,将表达式包装在 中(lambda () ...)),并将实际实现留给函数。这也有助于记录,因为两个相关的部分将独立地对宏如何扩展和如何运行进行注释。