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

发布于 2024-08-09 05:51:10 字数 1620 浏览 1 评论 0原文

所有这些都始于一个简单的想法:如何在 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

我相信我可以做一些事情来使ocaml字符串格式化代码像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 吗?代码:

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

    将生成错误信息:

    <块引用>

    错误:此表达式的类型为字符串 但需要一个表达式类型 ('a -> 'b, 单位, 字符串) 格式= ('a -> 'b,单位,字符串,字符串,字符串,字符串)格式6

All of these start from a simple idea: How to write python-style formatted string in ocaml.

pythoners could init a string as:

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

but ocaml's formatted string code as :

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

I believed I can do sth to make the ocaml string formating code python-like
At first, I defined a function as below:

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

then, I can write directly as:

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

but the simple function cannot handle more complex situations as two or more variables.
so I wanted to write a little complex function to make it perfectly simulate the python way.
I wrote it as below :

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

But the function just CANNOT work, because the type error and also ocaml's list cannot contains multiple types.
if you write sth like: "name: %s; age: %d" % ["John"; 20] the ocaml compiler world laugh at the code and tell you some type ERROR.

Obviously, I must use Tuple to replace List. but I just do NOT konw how to tail-recursive a variable-length tuple.

any suggestion is welcomed. I have two question indeed.

  1. how to write a pythonic ocaml code to format string.
  2. 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"

    would generate ERROR info as:

    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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

罗罗贝儿 2024-08-16 05:51:10

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

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."

This is actually doable if your operator starts with a # character, since that character has a higher precedence than function application.

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."
仄言 2024-08-16 05:51:10

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

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

它有效,但由于优先级所必需的括号而很难看。

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

(1) I don't think there's a good way to do it that is going to be nicer than using Printf.sprintf directly. I mean, you can extend what you've already come up with:

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

which works, but is ugly because of the parentheses that are necessary for precedence.

(2) It's really hard to generate a format string at runtime because format strings are parsed at compile time. They may look like string literals, but they are actually a different type, which is "something format6". (it figures out whether you want a string or format string based on the inferred type) In fact, the exact type of a format string depends on what placeholders are in the format; that's the only way that it is able to type-check the number and types of format arguments. It's best not to mess with format strings because they are tied very heavily into the type system.

又怨 2024-08-16 05:51:10

为什么要用一些动态格式替换静态检查的 sprintf ? OCaml 的 Printf 既使用紧凑又运行时安全。与 C printf 相比,C printf 紧凑但不安全,C++ 流安全但冗长。 Python 的格式并不比 C printf 更好(除了你得到的是异常而不是崩溃转储)。

唯一可以想象的用例是来自外部源的格式字符串。通常最好将其移至编译时。如果不可能,则只需回退到带有错误处理的手动动态格式化(正如已经说过的,您不能将 Printf 与动态格式字符串一起使用)。顺便说一句,这样的一个案例 - 国际化 - 包含在现有库中。一般来说,如果想要动态组合不同类型的多个值,则必须用变体(如 ['S "hello"; 'I 20])包装它们并在打印端进行模式匹配。

Why would one want to replace statically checked sprintf with some dynamic formatting? OCaml's Printf is both compact in usage and safe at runtime. Compare that to C printf which is compact but unsafe and C++ streams which are safe but verbose. Python's format is no way better than C printf (except that you get exception instead of crashdump).

The only imaginable use-case is format string coming from external source. And it is usually better to move it to compile-time. If it is not possible, then only one needs to fallback to manual dynamic formatting with error-handling (as already said you cannot use Printf with dynamic format string). BTW one such case - internationalization - is covered with existing libraries. Generally, if one wants to dynamically combine multiple values of different types, then one has to wrap them with variants (like ['S "hello"; 'I 20]) and pattern-match at printing side.

自由范儿 2024-08-16 05:51:10

您应该查看 OCaml Batteries Included 的扩展/可扩展 printf。我有一种感觉,你可以用它做你想做的事。

You should check out OCaml Batteries Included's extended/extensible printf. I have the feeling that you can do what you want with it.

嘿看小鸭子会跑 2024-08-16 05:51:10

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

让 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。

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"

What if we bypass the typing system...

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);;

I don't claim this to be the ultimate solution, but that's as far as I got after looking at the mailing list, http://pauillac.inria.fr/caml/FAQ/FAQ_EXPERT-eng.html#printf and ocaml's src.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文