假设你有一个语法树,其中包含代表标识符的节点。类似的东西:
type node = ...
| Ident of string
...
我想走树,并用标识符的出现次数标记所有Ident节点。例如,如果标识符“x”在树中出现3次,则每个Ident“x”节点将被标记为3。
显然,这需要修改类型decl以适应新数据(有些像“String of string * ref int”)。我的问题是这样做的最佳方法是什么?看起来像是一个进行计数的两遍方法,一个做“标记”的方法可能是最直接的,但即便如此,似乎跟踪给定标识符的节点也很尴尬。
连连呢?
答案 0 :(得分:0)
这是一个纯粹的功能性解决方案:
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