Похоже, ваш метод рекурсии по хвосту узла списка является проблемой. Вместо treeFun h
, добавленного к treefun (NODE(t))
, попробуйте использовать это для случая NODE:
treeFun (NODE(items)) = ["("] @ List.concat (map treeFun items) @ [")"]
То есть сопоставьте treeFun
всему содержимому узла и окружите результаты "("
и ")"
. Это определение может быть слишком кратким, чтобы вы могли понять, что происходит, поэтому вот более подробная форма, которая может показаться вам более понятной:
| 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
- это результат взятия всех поддеревьев в данном узле и превращения каждой из них в список строк путем рекурсивного вызова treeFun
для каждого поддерева. Поскольку treeFun
возвращает список строк каждый раз, когда он вызывается, и мы вызываем его для всего списка поддеревьев, результатом является соответствующий список списков поддеревьев. Так, например, если бы мы позвонили map treeFun [LEAF 1, LEAF 2, LEAF 3]
, мы бы вернулись [["1"], ["2"], ["3"]]
.
Это не тот ответ, который нам нужен, поскольку это список списков строк, а не список простых строк. Мы можем исправить это, используя List.concat
, который берет список списков и формирует единый список всех базовых элементов. Например, List.concat [["1"], ["2"], ["3"]]
возвращает ["1", "2", "3"]
. Теперь все, что нам нужно сделать, это поставить круглые скобки вокруг результата, и все готово.
Обратите внимание, что эта стратегия работает так же хорошо для полностью пустых узлов, как и для узлов с одним или несколькими поддеревьями, поэтому она устраняет необходимость во втором случае treeFun
в исходном определении. Как правило, в ML это запах кода, если функция одного аргумента не имеет точно один случай для каждого конструктора типа аргумента.