如何访问ocaml数据类型并递归修改值?

时间:2010-06-23 02:45:05

标签: ocaml types

我正在学习Ocaml并且完全失去了解决这个问题的方法。

这是一个例子。

让我们说

type xml = Element of tag * xml list | CharData of string;;

我希望访问标记值并对其进行修改。

我能想到的是

match xml with 
    Element (tag, xlist) -> (* do something *) 
|   CharData str -> (* do something *)

我知道这不是递归语法,但我想知道至少如何处理这个

3 个答案:

答案 0 :(得分:4)

如果我理解你的问题,你可以通过两种不同的方式实现你想要的目标。

您在标记类型上使用可变数据结构,如下例所示:

type tag = string ref;;
type xml = Element of tag * xml list | CharData of string;;

let xml_test = Element (ref "person",
   [CharData "text0";
    Element (ref "phoneNumber", [CharData "text2"]);
    CharData "text1";
    Element (ref "phoneNumber", [CharData "text3"])]);;

let modify_tag tag new_value=
    tag := new_value;;

let rec modify_and_print_xml xml =
    match xml with 
        Element (tag, xlist) -> modify_tag tag (!tag^"_modified");
                                print_string (!tag); print_newline ();

                                (*here you do the recursive call*)
                                List.iter (fun element -> modify_and_print_xml element) xlist

        |CharData str -> print_string str; print_newline ();;

modify_and_print_xml xml_test;;

否则,因为你不熟悉函数式编程,考虑它的最佳方法是不要修改标记值,而是构造一个具有修改标记的新xml值(这是你应该做的代码纯粹功能和消除副作用)。

这是一个例子,假设您想将名为“phoneNumber”的每个标签修改为“phone”:

let rec get_modified_xml xml =
    match xml with
        Element (tag, xlist) -> if (!tag = "phoneNumber") then
                                    Element(ref "phone", List.map (fun element -> get_modified_xml element) xlist)
                                else
                                    Element (tag, List.map (fun element -> get_modified_xml element) xlist)
        | _ -> xml;;


get_modified_xml xml_test;;

输出:

- : xml =
Element ({contents = "person"},
 [CharData "text0"; Element ({contents = "phone"}, [CharData "text2"]);
  CharData "text1"; Element ({contents = "phone"}, [CharData "text3"])])

答案 1 :(得分:2)

首先,“修改”是什么意思?是否可以只返回一个新的更改值,或者您是否真的必须改变原始结构?因为在OCaml中,唯一可变的东西是数组元素,字符串元素,显式标记为可变的记录中的字段(包括ref数据结构),以及明确标记为可变的对象中的字段。

答案 2 :(得分:1)

我认为你在这里松散地使用修改这个词 - 这对你没有任何过错。我怀疑那里有正确的人会在他们的递归数据结构中使用引用;并且是新的,我很难证明你的意思是字面意义。

存在差异,因为在函数式语言中,我们通常处理不可变数据结构。我认为你要问的是,如何返回一个新结构,其中指定的标签被另一个替换。除了Pedantry之外,在函数式语言中这是很自然的事情,它将是你很少考虑的事情。

让我们完全定义您正在使用的结构。我另外定义了标签 - 我假设它是一个字符串。

type tag = string
type xml = Element of tag * xml list | CharData of string;;

该功能的签名(所以我们很清楚我们要完成的工作),

val replace_tag : string -> string -> xml -> xml

let rec replace_tag from_tag to_tag = function
    (* nothing to do here... *)
    | (CharData _ ) as x -> x
    (* an element with the proper tag; call function on its contents *)
    | Element (tag, xmlist) when tag = tag_from ->
        let xmlist = List.map (fun t -> replace_tag from_tag to_tag t) xmlist in
        let ntag,ncontent = f tag xmlist in
        Element (tag_to,xmlist)
    (* look into each element of xml contents *)
    | Element (tag, xmlist) ->
        let xmlist = List.map (fun t -> replace_tag from_tag to_tag t) xmlist in
        Element(tag,xmlist)

对于我认为您的问题,这是一个体面,简单的解决方案。但是这有很多问题;如果该值不存在则不会进行错误检查,并且每次都会复制整个数据结构 - 从List.map调用开始。有了更多细节,我想我们可以为您提供更好的解决方案。