我想编写一个接受一对字符串并将第二个元素转换为格式的函数。我以为我可以使用format_of_string
函数,但它似乎不起作用。这是一个简化版本:
let pp fmt (e:string * string) =
let msg = format_of_string (snd e) in Format.fprintf fmt (" - "^^msg^^"@.");;
^^^^^^^
Error: This expression has type string but an expression was expected of type
('a, 'b, 'c, 'd, 'e, 'f) format6
有没有办法让我的函数接受一对字符串?
答案 0 :(得分:2)
我回答了我自己的问题但是我在上面的评论中找到了@Pascal Cuoq的解决方案。
问题是编译器只有在能够分析字符串以便计算格式类型时才能将字符串转换为格式。这就是format_of_string
仅处理已知字符串的原因。所以上面问题的第一个解决方案是在调用函数之前转换字符串,当它已知时,但它不是问题的真正答案。
最佳解决方案是使用Scanf.format_from_string
:
val format_from_string : string ->
('a, 'b, 'c, 'd, 'e, 'f) format6 -> ('a, 'b, 'c, 'd, 'e, 'f) format6
其中第二个参数的类型与预期格式相同。例如,在上面的示例中,因为字符串不包含任何%
参数,所以它将是:
let pp fmt (e:string * string) =
let msg = Scanf.format_from_string (snd e) "" in
Format.fprintf fmt (" - "^^msg^^"@.");;
然后调用此函数只对哪个字符串没有%
参数:
# Format.printf "%a" pp ("", "abc");;
- abc
- : unit = ()
但是否则会引发异常:
# Format.printf "%a" pp ("", "abc%d");;
Exception:
Scanf.Scan_failure "format read 'abc%d' does not match specification ''".
答案 1 :(得分:2)
违反直觉,format_of_string
不将string
转换为格式(由于格式类型是类型,因此无法以保证工作的方式执行此操作 - 在编译时检查,但在编译时不知道字符串的内容; Scanf.format_from_string
在运行时检查它。
相反,它的类型为val format_of_string : ('a, 'b, 'c, 'd, 'e, 'f) format6 -> ('a, 'b, 'c, 'd, 'e, 'f) format6
- 它采用格式并返回格式,这意味着它基本上是格式上的标识函数。
这种功能有什么用?对于OCaml中的表达式而言,OCaml中的字符串文字是“过载”的。它们可以是字符串类型或格式类型。大多数情况下,它被推断为字符串类型。但是,如果您在需要格式类型的上下文中使用它,例如Printf.printf
的参数,编译器将其推断为格式类型。但是如果你用它来初始化变量呢?编译器“默认”为字符串类型。但是,有时可能需要初始化格式类型的变量。您不能轻易使用类型注释,因为格式具有非常复杂的类型,您不希望必须明确指定它。相反,您可以通过format_of_string
传递字符串文字,它“强制”编译器将其推断为格式类型,否则返回值不变。
P.S。您是否可以在参数中使用格式而不是字符串?
答案 2 :(得分:1)
您不能将格式作为参数,并将其与其他字符串一起使用,并将其用作格式:类型系统不允许表达此格式。
想一想" - "
本来就是"%"
的情况。这是一个不同的例子,但它具有相同的类型,因此必须使用相同的机制接受或拒绝它。
以下行是否符合您原定的目的?
let pp fmt f x = Format.printf fmt " - %a@." f x ;;