printf - OCaml 中的 Printf 参数
问题描述
在 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
-like 函数是可变参数的,因为它们接受可变数量的参数。它不是真正特定于printf
家族的,您可以在 OCaml 中定义自己的可变参数函数,类型系统完全支持这一点。printf 的唯一魔力在于编译器将字符串文字翻译为例如"foo %d
类型的值format
。
现在,让我们看看printf
函数的类型,
('a, out_channel, unit) format -> 'a
请注意,它返回'a
的是类型变量。因为'a
可以是任何东西,它也可以是一个函数。是格式字符串的('a, out_channel, unit) format
类型,它定义了由该格式字符串生成的函数的类型。但重要的是要理解,尽管它"foo %d"
看起来像一个字符串,但实际上它是 type 的一个特殊内置值_ 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
-family 函数,例如,
# #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
您可能会发现使用or 或更好地使用Format.kasprintf
or来定义日志记录功能会更容易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 = ()
推荐阅读
- ios - 在 MVP 架构中将数据传递给 detailVC
- transactions - axi验证环境下testcase如何连接事务?
- pandas - 按时间戳对行进行排序
- tinymce - Tinymce - 如何在我的服务器上指定一个目录以在我插入图像时使用?
- helidon - io.helidon.webserver.ServerResponse.send() 在服务器上抛出错误但文件在前端下载
- kubernetes - Dapr 服务发现
- omnet++ - 隐式块序列化错误
- python - Python 线程 - 不断保存来自其他线程的结果
- sql - 分组方式未按预期对从字符串中提取的字段进行分组
- r - 为什么在 foreach 循环中定义的全局变量不适用于此后调用的函数?