2010-06-23 58 views
2

我正在學習Ocaml,完全沒有辦法處理這個問題。如何訪問ocaml數據類型並遞歸修改值?

這裏是一個例子。

假設

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

,我想訪問標記值並修改它。

我能想到的辦法是

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

我知道這是不是遞歸語法,但我想知道至少如何處理這個

回答

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的」到「手機」標籤:

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

那麼首先,你是什麼意思「修改」?返回一個新的改變的值是否可以,或者你真的需要改變原始結構嗎?因爲在OCaml中,唯一可變的是數組元素,字符串元素,明確標記爲可變的記錄中的字段(包括ref數據結構)以及明確標記爲可變的對象中的字段。

2

我覺得你用這個詞修改鬆散這裏 - 這對你沒有任何過錯。我懷疑任何有正確思想的人會在遞歸數據結構中使用引用;和新的,我很難證明你的意思是這樣的。

有一個區別,因爲在函數式語言中我們通常處理不可變的數據結構。我認爲你的意思是要問,是如何返回一個指定標籤替換另一個新結構。除此之外,在功能語言中這是一件非常自然的事情,而且這將是你很少考慮的事情。

讓我們完全定義您正在使用的結構。我另外定義了標籤 - 我假設它是一個字符串。

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。有了更多的細節,我認爲我們可以爲您提供更好的解決方案。