如何使用 OCaml 编译器打印表达式的内容

How to print the content of an expression using OCaml compiler

在 OCaml 中使用编译器时,我希望打印以下 3 个表达式的答案:

map (fun s -> s^"to") ["tak";"tok";"tek"] ;; 

map (fun x -> x >= 0) [ 7 ; -13; -103; 79; 1230 ];; 

map List.hd [ [false;false;true] ; [false;true;true]; [true;false;true]];;

当我使用 OCaml 顶级解释器时,我得到了所需的正确答案,分别是:

> map (fun s -> s^"to") ["tak";"tok";"tek"] ;; 
> - : string list = ["takto"; "tokto"; "tekto"]

> map (fun x -> x >= 0) [ 7 ; -13; -103; 79; 1230 ];; 
> - : bool list = [true; false; false; true; true]

> map List.hd [ [false;false;true] ; [false;true;true]; [true;false;true] ];;
> - : bool list = [false; false; true]

如何使用 OCaml 编译器复制它?如何编写完成工作所需的打印说明?

的确如此,OCaml 顶层 (REPL) 有一个通用打印机,在编译代码中不可用。

如果您刚刚开始使用 OCaml,我认为最好是手动编写您自己的打印函数。这将使您专注于学习语言的基础知识,而不是花哨的扩展方法。

例如,这是一个将字符串列表写入标准输出的函数:

let print_strings sl =
    let q s = "\"" ^ s ^ "\"" in
    Printf.printf "[ %s ]\n" (String.concat "; " (List.map q sl))

当然,字符串是最简单的情况,因为它们已经可以打印了。但是您可以非常相似地处理其他情况。

有一种方法可以使用 Printf.printf "%b"

打印布尔值

几乎1不可能,因为 OCaml 编译器会将程序翻译成二进制形式并删除类型信息。换句话说,程序编译后运行时,运行时只能看到机器整数,而没有关于该整数是否应被视为指向字符串、实整数、用户定义数据类型等的指针的信息.

事实上,许多编译语言都是如此,例如 C 或 C++。它们还要求用户提供用于打印其数据类型的特定函数,并在调用打印机时指定类型。

在 OCaml 中,我们通常使用 Format 模块中的 printf 函数进行打印。此函数与 C/C++ 中的相同函数非常相似(除了它是类型安全的),例如

open Format

let () = 
  printf "Hello, %s world\n%!" "cruel";
  printf "Hello, %d times!\n%!" 42

如您所见,特殊格式说明符,如 %s%d 指定参数的类型。所以 %d 需要一个 int 类型的参数,而 %s 需要 string 之一,还有 %c 用于 char%b 对于 bool 等等。此外,%!说明符代表flush,它会立即将信息输出到控制台设备(否则可能会被缓冲)。

还有一个通用的 %a 说明符,它不需要一个参数,而是两个 - 一个函数,它将告诉如何打印参数和参数本身。它用于打印抽象类型的值,因此得名 %a。有一个约定,定义抽象类型的模块还提供一个名为 pp 的函数,它指定应该如何打印这个值,例如,

let () = printf "Hello, abstract student %a\n%!" Student.pp s

其中 Student 模块可能定义为

struct Student : sig 
   type t 
   val create : string -> int -> t
   val pp : Format.formatter -> t -> unit
end = struct 
   type t = {name : string; cls : int}
   let create name cls  = {name; cls}
   let pp ppf {name; cls} = 
      Format.fprintf ppf "%s of %d" name cls
end

对于其他库提供的模块,您可以寻找 pp 作为通用打印机的功能。如果不存在,您也可以尝试将 to_string%s 说明符一起使用。

Format 模块(它是 OCaml 标准库的一部分)带有一些帮助程序,例如,有一个 pp_print_list 函数可用于为列表制作打印机,例如,

let pp_bool ppf x = fprintf ppf "%b" x

let pp_bools ppf xs = 
   pp_print_list pp_bool ppf xs
   ~pp_sep:(fun ppf () -> fprintf ppf ", ");

现在我们可以打印一个布尔值列表,

printf "(%a)\n" pp_bools [true; false; true]

会给我们,

(true, false, true)

您可能会注意到标准库中没有用于 Boolpp 打印机,甚至对此感到恼火。你不是一个人!例如,您可能会发现 fmt 库 (opam install fmt) 使打印变得更加有趣。您还可以使用各种派生预处理器来派生用户定义类型的打印机,请参阅 post 末尾的 link。但这对于高级用途来说,对于初学者来说,您必须掌握格式库。


1) 真实的故事总是比较复杂。 OCaml 仍然通过使用值标记来保留一些信息,这使得 genprintlib that provide generic printers that work for any objects even when the program is compiled to a native representation. It is quite a hacky project that relies on the inner and undocumented features of the runtime, so use it with care and for debugging only. See also this discussion

等项目成为可能