2.5 文档

66 阅读4分钟

OCaml 提供了一个名为 OCamldoc 的工具,它的工作方式与 Java 的 Javadoc 工具非常类似:它从源代码中提取特定格式的注释,并将它们渲染为HTML,使程序员更容易阅读文档。

如何记录文档

下面是一个 OCamldoc 注释的例子:

(** [sum lst] is the sum of the elements of [lst]. *)
(** [sum lst] 是 [lst] 中元素的和。*)
let rec sum lst = ...
  • 双星号使注释被识别为 OCamldoc 注释。

  • 注释里面的方括号表示这些部分应该在 HTML 中显示为 打字机字体,而不是常规字体。

和 Javadoc 一样,OCamldoc 也支持文档标记,如 @author、@deprecated、@param、@return 等。例如,在大多数编程任务的第一行,我们要求你完成这样的注释:

(** @author Your Name (your netid) *)
(** @author 你的名字 (你的网络标识(网名)) *)

有关 OCamldoc 注释中所有可能的标记,请参阅 OCamldoc 手册。但是对于大多数需要编写的文档来说,我们在这里介绍的内容已经足够了。

什么文档

本书推崇的文档风格类似于 OCaml 标准库:简洁并且是声明式的。举个例子,让我们重温 sum 的文档:

(** [sum lst] is the sum of the elements of [lst]. *)
(** [sum lst] 是 [lst] 中元素的和。*)
let rec sum lst = ...

该注释以 sum lst 开头,这是一个将函数应用于参数的示例。注释继续使用单词 “是”,以声明的方式描述应用的结果。(可以使用“返回”这个词,但“是”强调了函数的数学性质。) 该描述使用参数的名称 lst 来解释结果。

注意,不需要添加标签来冗余地描述参数或返回值,而这通常是用 Javadoc 完成的。该说的都说了。我们强烈反对像下面这样的文档:

(** Sum a list.
    @param lst The list to be summed.
    @return The sum of the list. *)
let rec sum lst = ...

这个糟糕的文档用了三行毫无必要且难以阅读的行来表达与简洁的单行版本相同的内容。

有一种方法可以改进目前已有的文档,那就是显式地说明空列表会发生什么:

(** [sum lst] is the sum of the elements of [lst].
    The sum of an empty list is 0. *)
(** [sum lst] 是 [lst] 中元素的和。
	空列表和为 0。*)
let rec sum lst = ...

前置条件和后置条件

下面是一些以我们喜欢的风格编写的注释示例。

(** [lowercase_ascii c] 返回与 [c] 对应的小写ASCII码。*)


(** [index s c] 返回字符串[s]中第一次出现的字符[c]的索引。
	如果[c]在[s]中没有出现,	则引发:Not_found]。*)


(** [random_int bound] 返回一个介于0(包括)和[bound](不包括)之间的随机整数。
	要求: [bound]大于0且小于2的30次方。*)

index 函数的文档说明了该函数会抛出异常,及异常是什么以及在什么条件下引发异常。(我们将在下一章更详细地讨论异常。) random_int 的文档指定函数的参数必须满足一个条件。

在之前的课程中,你们接触过前置条件和后置条件的概念。前置条件是指某段代码运行之前必须为真的条件;之后还有一个后置条件。

random_int 文档中的 "Requires" 子句是一种前置条件。它的意思是,random_int 函数的客户端负责保证 bound 的值是正确的。同样,该文档的第一句话是一种后置条件。它保证了函数返回的值。

index 文档中的“raise”子句是另一种后置条件。它保证函数会引发异常。注意,这个子句不是前置条件,尽管它声明了输入的条件。

注意,这些示例都没有 “Requires” 子句来说明输入的类型。如果你使用的是动态类型语言,比如 Python,这可能会是个惊喜。Python 程序员经常在文档中记录函数输入类型的前置条件。然而,OCaml 程序员却没有。这是因为编译器本身会进行类型检查,以确保不会将错误类型的值传递给函数。再次考虑 lowercase_ascii :尽管英文注释有助于向读者识别 c 的类型,但注释并没有像这样声明“Requires”子句:

(** [lowercase_ascii c] is the lowercase ASCII equivalent of [c].
    Requires: [c] is a character. *)
(** [lowercase_ascii c] 返回与 [c] 对应的小写 ASCII 码。
	要求: [c] 是一个字符。*)

这样的注释读起来对 OCaml 程序员来说非常不规范,他们读到注释会感到困惑,可能会想:“当然 c 是一个字符;编译器会保证这一点。写这句话的人到底是什么意思?是他们还是我遗漏了什么吗?”

注:本书是康奈尔大学 CS 3110 数据结构和函数式编程的教材。原书为英文版,在学习的过程中,根据自己的理解,翻译了一些,做一个记录,版权归原作者所有,如有侵权,请联系我删除。