OCaml 中的 Printf 参数
Printf arguments in OCaml
在 OCaml 中,我在传递应该适用于 Printf.printf
的参数时收到一个我不理解的错误。可能是因为我没有完全理解那个功能,但我无法确定什么不起作用。
首先,我定义一个函数(用于日志记录):
utop # let log verbosity level str =
if level <= verbosity then (
Printf.printf "\nLevel %i: " level;
Printf.printf str);;
val log : int -> int -> (unit, out_channel, unit) format -> unit = <fun>
一切似乎都很好,但后来我得到了这个:
utop # log 0 0 "%i" 0;;
Error: This function has type
int -> int -> (unit, out_channel, unit) format -> unit
It is applied to too many arguments; maybe you forgot a `;'.
尽管以下有效:
utop # Printf.printf;;
- : ('a, out_channel, unit) format -> 'a = <fun>
utop # Printf.printf "%i" 0;;
0- : unit = ()
那么,我如何定义一个函数来完成 log
打算做的事情?
编辑: 事实上,log 0 0 "%i" 0;;
看起来参数太多(4 个而不是 3 个),但 Printf.printf "%i" 0;;
也是如此(2 个而不是 1 个) , 它有效。通过部分应用,这给出了:
utop # Printf.printf "%i";;
- : int -> unit = <fun>
utop # log 0 0 "%i";;
Error: This expression has type (unit, unit) CamlinternalFormatBasics.precision
but an expression was expected of type
(unit, int -> 'a) CamlinternalFormatBasics.precision
Type unit is not compatible with type int -> 'a
类似 printf
的函数是可变的,因为它们接受可变数量的参数。它并不是真正特定于 printf
和家族,您可以在 OCaml 中定义自己的可变参数函数,这是类型系统完全支持的。 printf 的唯一魔力是编译器将字符串文字(例如 "foo %d
转换为 format
.
类型的值
现在,让我们看一下printf
函数的类型,
('a, out_channel, unit) format -> 'a
注意它 returns 'a
这是一个类型变量。由于 'a
可以是任何东西,它也可以是一个函数。 ('a, out_channel, unit) format
是定义此格式字符串生成的函数类型的格式字符串的类型。重要的是要理解,尽管 "foo %d"
看起来像一个字符串,但实际上它是一个特殊的 _ format
类型的内置值,它有一个看起来像字符串的文字(尽管并非所有有效字符串都是 _ format
类型的有效文字。
只是为了证明 printf
的第一个参数不是字符串,让我们尝试以下操作,
# Printf.printf ("foo " ^ "%d");;
Line 1, characters 14-29:
1 | Printf.printf ("foo " ^ "%d");;
^^^^^^^^^^^^^^^
Error: This expression has type string but an expression was expected of type
('a, out_channel, unit) format
现在,当我们知道 printf 不是一个典型的函数时,让我们自己定义一个类似 printf 的函数。为此,我们需要使用 kprintf
系列函数,例如
# #show Printf.ksprintf;;
val ksprintf : (string -> 'd) -> ('a, unit, string, 'd) format4 -> 'a
此函数采用接收结果字符串的函数,例如,
# let log fmt = Printf.ksprintf (fun s -> print_endline ("log> "^s)) fmt;;
val log : ('a, unit, string, unit) format4 -> 'a = <fun>
# log "foo";;
log> foo
- : unit = ()
这个生成的函数看起来更像 sprintf
,即,它将与使用字符串作为输出设备的漂亮打印函数很好地配合使用(这是一个不同的主题)。您可能会发现使用 Printf.kfprintf
或使用 Format.kasprintf
或 Format.kfprintf
更好地定义日志记录函数。后两个函数有以下类型,
val kasprintf : (string -> 'a) -> ('b, formatter, unit, 'a) format4 -> 'b
val kfprintf : (formatter -> 'a) -> formatter ->
('b, formatter, unit, 'a) format4 -> 'b
但是格式类型适用于 formatter
类型(输出设备的抽象),这是漂亮打印机(通常称为 pp
)正在接受的类型。因此使用 Format
模块定义的日志函数将更好地与现有库一起使用。
因此,使用 Format.kasprintf
我们可以将您的日志函数定义为,
# let log verbosity level =
Format.kasprintf (fun msg ->
if level <= verbosity then
Format.printf "Level %d: %s@\n%!" level msg);;
val log : int -> int -> ('a, Format.formatter, unit, unit) format4 -> 'a = <fun>
下面是它的使用方法,
# log 0 0 "Hello, %s, %d times" "world" 3;;
Level 0: Hello, world, 3 times
- : unit = ()
在 OCaml 中,我在传递应该适用于 Printf.printf
的参数时收到一个我不理解的错误。可能是因为我没有完全理解那个功能,但我无法确定什么不起作用。
首先,我定义一个函数(用于日志记录):
utop # let log verbosity level str =
if level <= verbosity then (
Printf.printf "\nLevel %i: " level;
Printf.printf str);;
val log : int -> int -> (unit, out_channel, unit) format -> unit = <fun>
一切似乎都很好,但后来我得到了这个:
utop # log 0 0 "%i" 0;;
Error: This function has type
int -> int -> (unit, out_channel, unit) format -> unit
It is applied to too many arguments; maybe you forgot a `;'.
尽管以下有效:
utop # Printf.printf;;
- : ('a, out_channel, unit) format -> 'a = <fun>
utop # Printf.printf "%i" 0;;
0- : unit = ()
那么,我如何定义一个函数来完成 log
打算做的事情?
编辑: 事实上,log 0 0 "%i" 0;;
看起来参数太多(4 个而不是 3 个),但 Printf.printf "%i" 0;;
也是如此(2 个而不是 1 个) , 它有效。通过部分应用,这给出了:
utop # Printf.printf "%i";;
- : int -> unit = <fun>
utop # log 0 0 "%i";;
Error: This expression has type (unit, unit) CamlinternalFormatBasics.precision
but an expression was expected of type
(unit, int -> 'a) CamlinternalFormatBasics.precision
Type unit is not compatible with type int -> 'a
类似 printf
的函数是可变的,因为它们接受可变数量的参数。它并不是真正特定于 printf
和家族,您可以在 OCaml 中定义自己的可变参数函数,这是类型系统完全支持的。 printf 的唯一魔力是编译器将字符串文字(例如 "foo %d
转换为 format
.
现在,让我们看一下printf
函数的类型,
('a, out_channel, unit) format -> 'a
注意它 returns 'a
这是一个类型变量。由于 'a
可以是任何东西,它也可以是一个函数。 ('a, out_channel, unit) format
是定义此格式字符串生成的函数类型的格式字符串的类型。重要的是要理解,尽管 "foo %d"
看起来像一个字符串,但实际上它是一个特殊的 _ format
类型的内置值,它有一个看起来像字符串的文字(尽管并非所有有效字符串都是 _ format
类型的有效文字。
只是为了证明 printf
的第一个参数不是字符串,让我们尝试以下操作,
# Printf.printf ("foo " ^ "%d");;
Line 1, characters 14-29:
1 | Printf.printf ("foo " ^ "%d");;
^^^^^^^^^^^^^^^
Error: This expression has type string but an expression was expected of type
('a, out_channel, unit) format
现在,当我们知道 printf 不是一个典型的函数时,让我们自己定义一个类似 printf 的函数。为此,我们需要使用 kprintf
系列函数,例如
# #show Printf.ksprintf;;
val ksprintf : (string -> 'd) -> ('a, unit, string, 'd) format4 -> 'a
此函数采用接收结果字符串的函数,例如,
# let log fmt = Printf.ksprintf (fun s -> print_endline ("log> "^s)) fmt;;
val log : ('a, unit, string, unit) format4 -> 'a = <fun>
# log "foo";;
log> foo
- : unit = ()
这个生成的函数看起来更像 sprintf
,即,它将与使用字符串作为输出设备的漂亮打印函数很好地配合使用(这是一个不同的主题)。您可能会发现使用 Printf.kfprintf
或使用 Format.kasprintf
或 Format.kfprintf
更好地定义日志记录函数。后两个函数有以下类型,
val kasprintf : (string -> 'a) -> ('b, formatter, unit, 'a) format4 -> 'b
val kfprintf : (formatter -> 'a) -> formatter ->
('b, formatter, unit, 'a) format4 -> 'b
但是格式类型适用于 formatter
类型(输出设备的抽象),这是漂亮打印机(通常称为 pp
)正在接受的类型。因此使用 Format
模块定义的日志函数将更好地与现有库一起使用。
因此,使用 Format.kasprintf
我们可以将您的日志函数定义为,
# let log verbosity level =
Format.kasprintf (fun msg ->
if level <= verbosity then
Format.printf "Level %d: %s@\n%!" level msg);;
val log : int -> int -> ('a, Format.formatter, unit, unit) format4 -> 'a = <fun>
下面是它的使用方法,
# log 0 0 "Hello, %s, %d times" "world" 3;;
Level 0: Hello, world, 3 times
- : unit = ()