2009-11-02 10 views
2

所有这些都从一个简单的想法开始:如何在ocaml中编写python样式的格式化字符串。OCaml问题:如何用pythonic方法构造一个对齐的字符串

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 

我相信我能做某事使OCaml的字符串格式化代码蟒蛇般 在首先,我定义了如下功能:

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?下面的代码:

    令S = “%s” 表示Printf.sprintf的 “你好”

    会产生错误信息为:

    Error: This expression has type string but an expression was expected of type ('a -> 'b, unit, string) format = ('a -> 'b, unit, string, string, string, string) format6

回答

5

(1)我不认为有一种很好的方法可以实现,比直接使用Printf.sprintf更好。我的意思是,你可以扩展你已经想出:

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

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

2

为什么要用一些动态格式替换静态检查的sprintf? OCaml的Printf在使用上紧凑且在运行时安全。比较一下,这是紧凑但不安全的C printf和安全但详细的C++流。 Python的格式不比C printf好(除了你得到异常而不是崩溃转储)。

唯一可以想象的用例是来自外部源的格式字符串。将它移到编译时通常会更好。如果这是不可能的,那么只有一个需要回退到手动动态格式化和错误处理(正如已经说过的,你不能使用带有动态格式字符串的Printf)。顺便提一下,这种情况 - 国际化 - 涵盖existing libraries。通常,如果想要动态合并不同类型的多个值,则必须在变体(如['S "hello"; 'I 20])和打印端的模式匹配中包装它们。

-1

If Ocaml cannot dynamically generate some string as format6 str and pass it to sprintf? for code:

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。

+2

绕过类型系统不是一个好主意。它导致程序崩溃或以其他方式行事。 – Gilles 2011-11-13 17:40:40

+0

“如果我们绕过打字系统......”,那么使用C或其他一些没有数学建立的碰撞保护的语言会更好。但你的研究是彻底的。 – 2015-10-21 17:13:16

1

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