It looks like your method of recursion over the tail of a list node is the problem. Instead of treeFun h
appended to treefun (NODE(t))
, try using this for the NODE case:
treeFun (NODE(items)) = ["("] @ List.concat (map treeFun items) @ [")"]
That is, map treeFun
over the entire contents of the node, and surround the results with "("
and ")"
. That definition might be a bit too terse for you to understand what's going on, so here's a more verbose form that you might find clearer:
| treeFun (NODE(items)) =
let val subtree_strings : string list list = map treeFun items
val concatenated_subtrees : string list = List.concat subtree_strings
in ["("] @ concatenated_subtrees @ [")"]
end
subtree_strings
is the result of taking all the subtrees in the given node, and turning each of them to a list of strings by recursively calling treeFun
on each subtree. Since treeFun
gives back a list of strings each time it's called, and we're calling it on an entire list of subtrees, the result is a corresponding list of lists of subtrees. So for instance, if we called map treeFun [LEAF 1, LEAF 2, LEAF 3]
, we'd get back [["1"], ["2"], ["3"]]
.
That's not the answer we want, since it's a list of lists of strings rather than a list of plain strings. We can fix that using List.concat
, which takes a list of lists, and forms a single list of all the underlying items. So for instance List.concat [["1"], ["2"], ["3"]]
returns ["1", "2", "3"]
. Now all we have to do is put the parentheses around the result, and we're done.
Notice that this strategy works just as well for completely empty nodes as it does for nodes with one or more subtrees, so it eliminates the need for the second case of treeFun
in your original definition. Generally, in ML, it's a code smell if a function of one argument doesn't have exactly one case for each constructor of the argument's type.