2017-08-01 68 views
0

我試圖給一棵樹的每個元素分配一個數字。我認爲使用refs會使任務更容易,但是我遇到了一個奇怪的行爲:分配的數字不是唯一的,並且沒有出現清晰的模式。我設法修復了這個bug(添加了let unboxed = !second_ref in行),但我不明白髮生了什麼。OCaml,與refs和樹的意外行爲

輸出控制檯中的第一棵樹只是確保print_tree函數輸出它應該。

但是,第二個打印的預期輸出應該與第三個樹完全相同。我錯過了什麼?

type ('a, 'b) tree = 
    | Node of 'a * ('a, 'b) tree * ('a, 'b) tree 
    | Leaf of 'b 

let print_tree tree string_of_node string_of_leaf = 
    let rec print indent tree = 
    match tree with 
    | Leaf (l) -> print_string (indent^" -> "^string_of_leaf(l)^"\n") 
    | Node (n, left, right) -> 
     Printf.printf "%s-----------\n" indent; 
     print (indent^"|   ") left; 
     Printf.printf "%s%s\n" indent (string_of_node(n)); 
     print (indent^"|   ") right; 
     Printf.printf "%s-----------\n" indent 
    in print "" tree 

let myTree = Node(1,Node(2,Leaf(3),Leaf(4)),Node(5,Leaf(6),Leaf(7))) ;; 

let first_ref = ref 0 ;; 
let rec bug tree = 
    first_ref := !first_ref+ 1; 
    match tree with 
    |Leaf(a) -> Leaf(!first_ref) 
    |Node(n,l,r) -> Node(!first_ref, bug l, bug r) ;; 

let second_ref = ref 0 ;; 
let rec bug_fixed tree = 
    second_ref := !second_ref + 1; 
    let unboxed = !second_ref in 
    match tree with 
    |Leaf(a) -> Leaf(unboxed) 
    |Node(n,l,r) -> Node(unboxed, bug_fixed l, bug_fixed r) ;; 


let bug_tree = bug myTree ;; 
let bug_fixed_tree = bug_fixed myTree ;; 

print_tree myTree string_of_int string_of_int ; 
print_tree bug_tree string_of_int string_of_int ; 
print_tree bug_fixed_tree string_of_int string_of_int ; 

輸出如下:

----------- 
|   ----------- 
|   |   -> 3 
|   2 
|   |   -> 4 
|   ----------- 
1 
|   ----------- 
|   |   -> 6 
|   5 
|   |   -> 7 
|   ----------- 
----------- 
----------- 
|   ----------- 
|   |   -> 7 
|   7 
|   |   -> 6 
|   ----------- 
7 
|   ----------- 
|   |   -> 4 
|   4 
|   |   -> 3 
|   ----------- 
----------- 
----------- 
|   ----------- 
|   |   -> 7 
|   5 
|   |   -> 6 
|   ----------- 
1 
|   ----------- 
|   |   -> 4 
|   2 
|   |   -> 3 
|   ----------- 
----------- 
+0

這可能是題外話這裏,但你tree'讓我爲難的類型'的定義。葉子可能與節點有不同的類型? – RichouHunter

回答

6

在你bug功能,有此問題的表達:

Node(!first_ref, bug l, bug r) 

其行爲依賴的參數評估的順序:bug lbug r增加first_ref,所以傳遞的值可能不是你想要的。

您可以通過執行例如強制命令:

let v = !first ref in 
let new_l = bug l in 
let new_r = bug r in 
Node (v, new_l, new_r) 
+0

只需爲此答案添加一點上下文。從理論上講,由於沒有副作用,評估順序在純粹的功能語言中並不重要。當然,使用引用打破了這種情況,因此是局部綁定技巧。值得注意的是,在OCaml中沒有規定評估順序,這與語言的功能性相一致。 – RichouHunter

+3

@RichouHunter,OCaml遠不是純粹的,除了可變狀態之外還有很多其他的效果,例如,例外,非終止,I/O等等。恕我直言,沒有指定評估順序的藉口。這是它最令人討厭的陷阱之一。 –

+0

我完全同意@AndreasRossberg。不過,我想知道現在指定它的影響是否會影響現有的實現和代碼庫。 – RichouHunter