OCaml:如何以pythonic方式构造格式化字符串?

时间:2009-11-02 18:25:00

标签: ocaml python

所有这些都从一个简单的想法开始:如何在ocaml中编写python样式的字符串。

pythoners可以将字符串初始化为:

str = "this var: %s" % this_var
str2 = "this: %s; that: %s" % (this_var, that_var)

但ocaml的格式化字符串代码为:

let str = Printf.sprintf "this var: %s" this_var
let str2 = Printf.sprintf "this: %s; that: %s" this_var that_var

我相信我可以做ochl字符串格式化代码python 首先,我定义了一个函数如下:

let (%) s x = Printf.sprintf s x

然后,我可以直接写为:

let str = "this: %s" % "sth"

但是简单函数不能将更复杂的情况作为两个或更多变量处理。 所以我想编写一个复杂的函数来使它完美地模拟python方式。 我写的如下:

let (%) s li = 
  let split_list = Str.full_split (regexp "%[a-z]") s in
  let rec fmt result_str s_list x_list = match s_list with
    | [] -> result_str
    | shd::stl -> match shd with
       | Text t ->  fmt (result_str^t) stl x_list
       | Delim d -> match x_list with
          | [] -> fmt result_str stl []
          | xhd::xtl -> fmt (result_str^(Printf.sprintf d xhd)) stl xtl
  in 
  fmt "" split_list li

但该功能只是不能正常工作,因为类型错误和ocaml的列表不能包含多种类型。 如果你写得像"name: %s; age: %d" % ["John"; 20]那么ocaml编译器世界嘲笑代码并告诉你某种类型的错误。

显然,我必须使用Tuple来替换List。但我只是不知道如何对可变长度元组进行尾递归。

欢迎任何建议。我确实有两个问题。

  1. 如何编写pythonic ocaml代码来格式化字符串。
  2. 如果Ocaml无法动态生成某些字符串作为format6 str和 把它传递给sprintf?代码:

    在Printf.sprintf s“hello”

    中让s =“%s”

    会生成ERROR信息:

      

    错误:此表达式具有类型字符串   但预计会有类型的表达            ('a - >'b,unit,string)format =              ('a - >'b,unit,string,string,string,string)format6

5 个答案:

答案 0 :(得分:5)

(1)我认为没有比直接使用Printf.sprintf更好的方法。我的意思是,你可以扩展你已经提出的内容:

let (%) = Printf.sprintf
let str3 = ("this: %s; that: %s; other: %s" % this_var) that_var other_var

有效,但由于优先权所必需的括号,因此很难看。

(2)在运行时生成格式字符串真的很难,因为格式字符串在编译时被解析。它们可能看起来像字符串文字,但它们实际上是一种不同的类型,这是“格式6”。 (它根据推断类型确定您是否需要字符串或格式字符串)事实上,格式字符串的确切类型取决于格式中的占位符;这是它能够键入检查格式参数的数量和类型的唯一方法。最好不要混淆格式字符串,因为它们与类型系统密切相关。

答案 1 :(得分:2)

为什么要用静态格式替换静态检查的sprintf? OCaml的Printf使用紧凑,运行时安全。将它与C printf相比较,C printf是紧凑但不安全的,C ++流是安全但冗长的。 Python的格式绝不比C printf好(除了你得到异常而不是crashdump)。

唯一可以想象的用例是来自外部源的格式字符串。通常最好将其移动到编译时。如果不可能,那么只有一个需要回退到手动动态格式化并进行错误处理(如前所述,您不能将Printf与动态格式字符串一起使用)。 BTW就是这样一个案例 - 国际化 - 由existing libraries覆盖。通常,如果想要动态组合不同类型的多个值,则必须使用变体(如['S "hello"; 'I 20])和打印方面的模式匹配来包装它们。

答案 2 :(得分:1)

你应该查看OCaml Batteries Included的扩展/可扩展printf。我觉得你可以用它做你想做的事。

答案 3 :(得分:1)

如果您的运算符以#字符开头,这实际上是可行的,因为该字符的优先级高于函数应用程序。

let (#%) = Printf.sprintf;;
val ( #% ) : ('a, unit, string) format -> 'a = <fun>
"Hello %s! Today's number is %d." #% "Pat" 42;;
- : string = "Hello Pat! Today's number is 42."

答案 4 :(得分:0)

  

如果Ocaml无法动态生成   一些字符串作为format6 str并传递它   sprintf?代码:

     

let s = "%s" in Printf.sprintf s "hello"

如果我们绕过打字系统怎么办......

external string_to_format :
 string -> ('a, 'b, 'c, 'd, 'e, 'f) format6 = "%identity"

let s = string_to_format "%s" in (Printf.sprintf s "hello" : string);;

我并不认为这是最终的解决方案,但在查看邮件列表http://pauillac.inria.fr/caml/FAQ/FAQ_EXPERT-eng.html#printf和ocaml的src之后,这是我所得到的。

相关问题