【问题标题】:Why doesn't this short OCaml snippet with Printf.printf work?为什么这个带有 Printf.printf 的简短 OCaml 片段不起作用?
【发布时间】:2010-03-05 18:16:15
【问题描述】:

我是 OCaml 新手。我正在玩“hello world”类型的 sn-ps 并遇到了这种情况。 这是一个带有一些额外 cmets 的解释器会话:

# let average a b = 
  (a +. b) /. 2.;;
val average : float -> float -> float = <fun>
# average 1. 4.;;
- : float = 2.5
# string_of_float (average 1. 4.);;
- : string = "2.5"

(* this fails...*)
# let _ = Printf.printf (string_of_float (average 1. 4.));;
Error: This expression has type string but an expression was expected of type
         ('a, out_channel, unit) format =
           ('a, out_channel, unit, unit, unit, unit) format6

(* yet this works *)
# "hello!";;
- : string = "hello!"
# let _ = Printf.printf "hello!";;
hello!- : unit = ()

(* another failed attempt *)
# let s = string_of_float (average 1. 4.);;
val s : string = "2.5"
# s;;
- : string = "2.5"
# let _ = Printf.printf s;;
Error: This expression has type string but an expression was expected of type
         ('a, out_channel, unit) format =
           ('a, out_channel, unit, unit, unit, unit) format6

(* and this also works?? *)
# let _ = Printf.printf "2.5";;
2.5- : unit = ()

所以情况就是这样。 string_of_float (average 1. 4.) 返回一个字符串, 就像"hello!" 一样。当我将"hello!" 输入Printf.printf 时,它可以工作 正如预期的那样。当我将string_of_float (average 1. 4.) 给Printf.printf 它失败并告诉 我预期的不是字符串,而是其他奇怪的类型。但是为什么"hello!" 和"2.5" 会起作用呢?

发生了什么事?

【问题讨论】:

    标签: printf ocaml


    【解决方案1】:

    OCaml 中有一种字符串字面量含义的“重载”。在编译时,它们可以被解释为字符串,也可以被解释为格式(在类型系统中它们是完全不同的东西),这取决于类型检查器的想法。如果它决定它应该是一种格式,那么在编译时直接解析格式字符串(这就是它能够在编译时对 printf 的参数进行类型检查的原因)。 (与 C 不同,它在运行时解析字符串。)但是,没有简单的方法可以在运行时将字符串转换为格式。因此,当您看到 Printf.printf "2.5" 时,“2.5”实际上不是字符串,而是在编译时解析的特殊格式类型。这就是为什么你不能用一个字符串代替它。

    在不相关的注释中,如果您只想打印一个字符串,您可能需要使用print_string(或者如果您想要换行符,则使用print_endline)。

    【讨论】:

    • 这对我来说很有意义。谢谢!
    • 无法在运行时构造format6 真的是……缺乏。有什么方法(除了自己重写)来实现同样的目标吗? (例如,如何在运行时用任意数量的空格填充字符串?在 C 中我们可以sprintf(sprintf("%%%ds", width), str)。
    • @kizzx2:对于这个特定任务,有一个替代解决方案:格式字符串语法允许用星号替换宽度或精度,在这种情况下,它将从一个额外的参数中获取它在主要论点之前:Printf.sprintf "%*s" width str;这种语法在 C99、Perl、Python、Ruby、Go 等中也可用。当然这只是一种特殊情况;对于其他事情,您可能会不走运。 OCaml 中有一个运算符^^ 将格式附加在一起;但是您仍然需要首先附加格式,所以我不知道这有多大用处。
    • 很高兴知道新的语法 :) 虽然 OCaml 的类型系统在这种情况下似乎真的显示出它的弱点。
    【解决方案2】:
    Printf.printf "%s" anyStringExpr
    

    会起作用。 printf 的第一个参数有点神奇。 (其他人会填写详细信息。)

    【讨论】:

    • 是的,第一个参数是一个包含指令的格式字符串,并且在应用时返回一个以该数量的指令作为参数的函数。绝对神奇。
    • 那种。无论如何,拥有一个经过完全类型检查的 printf 工具真的很棒。
    • 嗯...这引发了更多问题。从功能的角度来看,我希望如果 f(x) = y,g(f(x)) = g(y)。为什么这不适用于 printf?它与 printf 本质上必须有副作用的事实有关吗?这种“魔法”常见吗?
    • @dimatura 它与副作用无关,与(Printf.printf fmt) 的类型取决于fmt 的内容 的事实有关。对于普通函数,(f x) 的类型只取决于f 和x 的类型。在类型系统中没有其他如此大规模的 hack(尽管它仍然是安全的,只是有点奇怪)。
    • @Pascal 我想我更了解正在发生的事情。很高兴知道这种行为并不普遍。
    猜你喜欢
    • 2011-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-26
    • 1970-01-01
    • 2014-07-31
    相关资源
    最近更新 更多