Как получить доступ к типу данных ocaml и рекурсивно изменить значение? - PullRequest
2 голосов
/ 23 июня 2010

Я изучаю Ocaml и совершенно потерял, как с этим справиться.

Вот пример.

Допустим,

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

, и я хочу получить доступ к тегуоцените и измените его.

Я могу думать о том, как это

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

Я знаю, что это не рекурсивный синтаксис, но я хочу знать хотя бы, как справиться с этим

Ответы [ 3 ]

4 голосов
/ 23 июня 2010

Если я понимаю ваш вопрос, вы можете достичь того, чего хотите, двумя разными способами.

Вы используете изменяемую структуру данных для вашего типа тега, как в следующем примере:

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"])])
2 голосов
/ 23 июня 2010

Ну, во-первых, что вы подразумеваете под "изменить"? Можно ли просто вернуть новое измененное значение, или вам действительно нужно изменить исходную структуру? Потому что в OCaml единственными объектами, которые могут изменяться, являются элементы массива, строковые элементы, поля в записях, явно помеченные как изменяемые (включая структуру данных ref), и поля в объектах, явно помеченные как изменяемые.

1 голос
/ 23 июня 2010

Я думаю, что вы используете слово «свободно» здесь - и это не вина против вас. Я сомневаюсь, что кто-то в здравом уме собирается использовать ссылки в своей рекурсивной структуре данных; и будучи новичком, мне трудно оправдать, что вы подразумеваете это буквально как таковое.

Есть разница, потому что в функциональных языках мы обычно имеем дело с неизменяемыми структурами данных. Я думаю, что вы хотите спросить, как вернуть новую структуру с указанным тегом, замененным другим. Помимо педантизма, это очень естественно делать на функциональных языках, и это будет то, что вы будете рассматривать редко.

Позволяет полностью определить структуру, с которой вы работаете. Я дополнительно определил тег - я предполагаю, что это строка.

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. С некоторыми подробностями, я думаю, мы сможем предложить вам лучшее решение.

...