我正在尝试编写一个模块,将 x:int = if true then 3 else 5 之类的内容转换为字符串
这是我到目前为止的代码
module Ast =
struct
type typ = Bool | Int
type var = A | B | C | D | E | F
type exp = Const of int * typ
| App of string * exp list
| If of exp * exp * exp
| And of exp * exp
| Or of exp * exp
| Id of var * typ * exp
let rec toString (t) =
let formatDec1(va,ty,e) = ???
match t with
Const(n, _) -> print_int n
| App(id, [e1; e2]) -> formatter(" " ^ id ^ " ", e1, e2)
| App(id, [e1]) -> formatter(" " ^ id ^ " ", e1, Const(0, Int))
| App(id, _) -> formatter(" " ^ id ^ " ", Const(0, Int), Const(0, Int))
| If(e1, e2, e3) -> formatIf(e1, e2, e3)
| And(e1, e2) -> formatter(" && ", e1, e2)
| Or(e1, e2) -> formatter(" || ", e1, e2)
| Id(va,ty,e) -> formatDecl(va,ty,e)
end
我还是 OCaml 的初学者,在网上找不到任何有关转换为字符串的信息。谢谢!
您想要使用 ml 语法漂亮地打印符号表达式。有一些库可以处理所谓的 sexp,但我从未使用过它们,我无法评论它们的用途,而且它可能不是您想要的,因为它会回避练习的目的。
我认为您与已有的内容有些接近,但您的代码中存在一些语法错误。我会给你很多建议来帮助你缩小差距。
首先,关于OCaml语法:
回想一下,您的函数参数不需要使用括号。函数定义的一般语法是:
let fun_name arg1 arg2 arg3 = function_body
如果你写
let fun_name (arg1,arg2,arg3) = ...
您实际上定义了一个带有 1 个参数的函数,该函数本身就是一个包含 3 个值的元组。
在表达式中,您可以使用
let ... in
构造定义局部值,就像定义顶级值时一样(就像我上面所做的那样)。如果这些值定义不相互依赖,您可以在多个值定义之间使用 and
关键字。
举个例子,
let x = 1
and y = "a" in
...
将定义2个值
x
和y
,可以在后续代码中使用。
在你的代码中
您所拥有的类型提供了一些符号表达式语言,您可以使用它来定义表达式,例如您在示例中给出的表达式:
“x:int = if true then 3 else 5”将是:
Id("x",
Int,
(If (Const (1, Bool),
Const (3, Int),
Const (5, Int))))
确实,您需要递归地遍历该值,并将返回的字符串组合成一个更大的字符串。或者,您可以使用字符串列表,并在末尾进行字符串连接。 看看这个例子,你可能会明白这是如何工作的:
"x" ^ (":" ^ "int") ^ "=" ^ ("if" ^ ("true") ^ "then" ^ ("3") ^ ("5"))
您可以在上面看到不同的子表达式模式。我用括号来表示它们。
在你的toString函数中,有3个未定义的值:
formatter
(采用 string * exp * exp
类型的 3 元组的函数)formatDec1
(另一个采用 exp * exp * exp
类型的 3 元组的函数)formatIf
(与formatDec1
相同)您在
rec
函数定义中使用了关键字 toString
,表明该函数是递归的。在我看来,你不需要上面的 3 个函数(它们是你最初分配的一部分,还是你自己定义的?),而只需要 toString
函数和字符串连接。
App
类型的exp
臂非常通用:您可以定义任意数量的参数的应用程序,因此您的字符串转换代码应该考虑这种可能性。您可以使用一些列表功能(想到fold_left
)来处理它。
还有一个名为deriving的项目可以帮助自动生成打印函数。我没用过,不过看起来不错。
如果看到这样的东西添加到基本 OCaml 编译器中,那就太酷了。它在 Haskell 中运行得很好(这就是“派生”这个名字的由来)。
您必须为您的类型(或由可打印类型的记录或产品组成的聚合类型)实现自己的打印例程。
您可能想要使用模块
Printf的
%a
转换规范,它除了要打印的参数之外还接受打印函数。
如果使用交互式
ocaml
顶级解释器,您可能需要使用 #install_printer
toplevel 指令。
在您的代码中,您想要递归调用
format
(例如,对于 And(e1, e2)
模式情况)。
要打印到字符串,请使用
Printf.sprintf
或 Format.sprintf
等。 Printf
和 Format
模块都为您提供了其他功能(打印到通道、缓冲区、字符串等)