Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/extjs/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
OCaml中的用户定义打印机_Ocaml_Printf_Pretty Print - Fatal编程技术网

OCaml中的用户定义打印机

OCaml中的用户定义打印机,ocaml,printf,pretty-print,Ocaml,Printf,Pretty Print,printf,fprintf等:都接受%a转换 手册上说对于%a: type ty = A | B let ty_to_string = function | A -> "A" | B -> "B" let print_ty chan v = output_string chan (ty_to_string v) let _ = Printf.printf "%a" print_ty A “用户定义的打印机。接受两个参数,并将第一个参数应用于outchan(当前输出通

printf
fprintf
等:都接受
%a
转换

手册上说对于
%a

type ty = A | B

let ty_to_string = function
  | A -> "A"
  | B -> "B"

let print_ty chan v = output_string chan (ty_to_string v)

let _ = Printf.printf "%a" print_ty A
“用户定义的打印机。接受两个参数,并将第一个参数应用于outchan(当前输出通道)和第二个参数。因此,第一个参数必须键入out_channel->'b->unit和第二个'b。因此,函数生成的输出将插入fprintf在当前点的输出。”

我无法理解用户定义打印机的用途,以及您将如何实现和使用它。有人能解释一下动机,并举个例子吗

例如,当您想要打印一个复杂的数据结构时,为什么不可能直接将带有自定义函数的数据结构打印到字符串或输出

你所说的“只打印复杂的数据结构”是什么意思?一旦定义了将数据结构转换为字符串的函数,就可以这样做。也可以使用“默认表示”来“转储”数据结构(请参阅),但这比调试更重要

话虽如此,;
%a
的一个非常简单的示例:

type ty = A | B

let ty_to_string = function
  | A -> "A"
  | B -> "B"

let print_ty chan v = output_string chan (ty_to_string v)

let _ = Printf.printf "%a" print_ty A
如果您有一个函数ty
->string
,您可以将它与
“%s”
一起使用来打印数据,因此我认为在实际情况下,您可以“只打印数据结构”。使用
“%a”
可能是一种风格选择。在某些方面,这似乎更为一致


在32位系统上,字符串的长度限制在16MB左右。因此,您可以想象这样一种情况,
%a“
将工作,而
%s”
将失败:如果中间字符串比这个长。不过,我从来没有在实践中想到过这一点。我自己只使用
%s

使用
%a
允许打印直接进入输出通道,而不是“%s”并将要打印的值字符串化然后打印

区别似乎完全是一个效率问题——既然可以且合理地将序列化数据直接发送到输出通道,为什么还要分配一个潜在的大字符串(或者使用一个缓冲区,并对其进行指数级调整和复制)?Jeffrey非常正确地指出,由于字符串长度的原因,在32位系统上非常大的序列化可能会失败

我经常在代码中使用
%a
,使用电池的可组合打印功能为我的值构建自定义打印机:

let range_print oc r = 
  let print_one oc (a,b) = fprintf oc "%d:%d" a b in
  List.print ~first:"" ~last:"" ~sep:"," print_one oc r
let print_rule print_pred print_dec oc r = 
  fprintf oc "%a,%a" print_pred r.pred print_dec r.dec
let print_item oc x = print_rule range_print Int.print oc x in
...
printf "IN : %a\nOUT: %a\n" print_item a print_item b;

我很难理解的是,为什么人们更喜欢这样说,而不是说let=Printf.Printf“%s”(tyu到字符串A)或let=Printf.fprintf stdout“%s”(tyu到字符串A)。至少后者还提供了某种关于打印到哪个通道的抽象,类似于在上面的示例中使用%a?问题是,对于更大的结构,转换为字符串可能代价高昂(想想:字符串连接)。如果可能的话,直接写入通道会更加高效。当你有这样的函数时,
%a
会自然而然地出现。
sprintf
ksprintf
取一个
unit->'a->string
的函数,与你提到的函数不同。我的问题可能很不精确。我想知道的是,在什么情况下,人们应该更喜欢使用
%a
而不仅仅是打印到字符串(使用
printf
%s
sprintf
ksprintf
等),然后使用字符串做其他事情,例如在通道上输出。