Functional programming 两次传递来标记语法树中的出现?
假设您有一个语法树,其中包含表示标识符的节点。比如:Functional programming 两次传递来标记语法树中的出现?,functional-programming,ocaml,Functional Programming,Ocaml,假设您有一个语法树,其中包含表示标识符的节点。比如: type node = ... | Ident of string ... 我要遍历树,并用该标识符的出现次数标记所有Ident节点。例如,如果标识符x在树中出现3次,则每个Ident x节点都将标记为3 显然,这需要修改decl类型以适应新的数据,比如字符串的Ident*ref int。我的问题是什么是实现这一点的最佳方法?似乎是一种两次通过的方法——一个进行计数,一个进行标记——可能是最简单的方法,但即使如此,跟踪给定标识符的节点似乎也
type node = ...
| Ident of string
...
我要遍历树,并用该标识符的出现次数标记所有Ident节点。例如,如果标识符x在树中出现3次,则每个Ident x节点都将标记为3
显然,这需要修改decl类型以适应新的数据,比如字符串的Ident*ref int。我的问题是什么是实现这一点的最佳方法?似乎是一种两次通过的方法——一个进行计数,一个进行标记——可能是最简单的方法,但即使如此,跟踪给定标识符的节点似乎也会很尴尬
建议 这里是一个纯功能解决方案:
type 'a exp = Ident of 'a | Const of int | Add of 'a exp * 'a exp
(* pass 1: count occurrences of identifiers *)
let count : string exp -> (string * int) list =
let rec record x = function
| [] -> [(x, 1)]
| (y, n) :: counts when x = y -> (y, n + 1) :: counts
| count :: counts -> count :: record x counts
in
let rec visit acc = function
| Ident x -> record x acc
| Const _ -> acc
| Add (e1, e2) -> visit (visit acc e1) e2
in
visit []
(* pass 2: mark all identifiers with their occurrence counts *)
let mark (counts : (string * int) list) : string exp -> (string * int) exp =
let rec lookup x = function
| [] -> invalid_arg "lookup"
| (y, n) :: _ when x = y -> n
| _ :: counts -> lookup x counts
in
let rec visit = function
| Ident x -> Ident (x, lookup x counts)
| Const n -> Const n
| Add (e1, e2) -> Add (visit e1, visit e2)
in
visit
(* combining the two passes *)
let mark_with_counts e = mark (count e) e
请注意,两个通行证可合并为一个单一的就诊功能,有效避免重复模式匹配:
let mark_with_counts' e =
let rec record x = function
| [] -> [(x, 1)]
| (y, n) :: counts when x = y -> (y, n + 1) :: counts
| count :: counts -> count :: record x counts
in
let rec lookup x = function
| [] -> invalid_arg "lookup"
| (y, n) :: _ when x = y -> n
| _ :: counts -> lookup x counts
in
let rec visit acc = function
| Ident x -> (record x acc, fun counts -> Ident (x, lookup x counts))
| Const n -> (acc, fun _ -> Const n)
| Add (e1, e2) ->
let (acc, syn1) = visit acc e1 in
let (acc, syn2) = visit acc e2 in
(acc, fun counts -> Add (syn1 counts, syn2 counts))
in
let (counts, syn) = visit [] e in
syn counts
为什么还要修改所有标识符节点?这是过度冗余的,并没有比边上的string->int映射方便多少。