5

我在OCaml中实现了一个简单的C语言语言,并且像往常一样,AST是我的中间代码表示形式。因为我会在树上做相当多的遍历,所以我想实现 访客模式来缓解疼痛。我的AST当前遵循该语言的语义:OCaml访问者模式

type expr = Plus of string*expr*expr | Int of int | ... 
type command = While of boolexpr*block | Assign of ... 
type block = Commands of command list 
... 

现在的问题是,树中的节点是不同类型的。理想情况下,我会传递给访问过程的一个单一函数处理一个节点的 ;该过程将切换节点的类型并相应地完成工作。现在,我必须为每个节点类型传递一个函数,这似乎不是最佳解决方案。

在我看来,我可以(1)真正采用这种方法或(2)只有一种类型以上。通常的方法是什么?也许使用OO?

+0

你能指定你想要的函数的类型吗? –

回答

10

没有人在功能语言中使用访问者模式 - 这是件好事。通过模式匹配,幸运的是,只需使用(相互)递归函数,就可以更轻松,直接地实现相同的逻辑。

例如,假设你想写一个简单的解释为您的AST:

let rec run_expr = function 
    | Plus(_, e1, e2) -> run_expr e1 + run_expr e2 
    | Int(i) -> i 
    | ... 

and run_command = function 
    | While(e, b) as c -> if run_expr e <> 0 then (run_block b; run_command c) 
    | Assign ... 

and run_block = function 
    | Commands(cs) = List.iter run_command cs 

Visitor模式通常只此复杂,尤其是当结果类型是异构的,喜欢这里。

+0

+1访客在这个实现方面的一个次要逻辑优势(我仍然会选择)是它为所有访问操作提供了一个通用接口。 – Mau

+2

@Mau,是的,但我不确定那会给你买什么。该接口既没有被指定,也没有详细说明(完全通用的类型不能说明任何内容)和过度指定(即使在许多情况下你不需要全部访问方法,也可以访问所有情况的方法)。更重要的是,对于所有可能的访问者,想要建立什么样的通用抽象? –

+0

感谢您的回答。我知道模式匹配给了我这种能力。所以,从本质上来说,每次我需要它来进行某些功能时,再遍历遍历树遍历都比较容易,而不是实现访问者模式。 – bellpeace

5

确实有可能使用每种AST类型的访问方法(默认情况下什么都不做)来定义一个类,并让您的访问函数将此类的一个实例作为参数。事实上,这种机制在OCaml世界中使用,尽管并不常见。

特别是,CIL library有一个访客类 (请参阅https://github.com/kerneis/cil/blob/develop/src/cil.mli#L1816的接口)。请注意,CIL的访问者本质上是必要的(转换已经完成)。然而,完全有可能定义将AST映射到另一个的访问者,例如Frama-C,它基于CIL并提供就地和复制访问者。最后,Cαml,AST生成器意味着轻松处理绑定变量,生成地图并将数据类型与访客一起折叠。

4

如果您必须通过一组相互递归数据类型(例如AST)编写许多不同的递归操作,那么您可以使用开放递归(以类的形式)对遍历进行编码并为自己节省一些锅炉板。

Real World OCaml有这样一个访客类的例子。