2017-08-01 2 views
0

Ich habe versucht, jedem Element eines Baums eine Zahl zuzuweisen. Ich dachte, mit refs würde die Aufgabe einfacher, aber ich stieß auf ein seltsames Verhalten: die Nummern zugewiesen waren nicht eindeutig und kein klares Muster entstand. Ich habe es geschafft, den Fehler zu beheben (Hinzufügen der let unboxed = !second_ref in Zeile), aber ich verstehe nicht, was passiert ist.OCaml, unerwartetes Verhalten bei Refs und Bäumen

Der erste Baum in der Ausgabekonsole stellt nur sicher, dass die print_tree Funktion ausgibt, was es sollte.

Die für den zweiten Druck erwartete Ausgabe sollte jedoch genau der gleiche sein wie der dritte Baum. Was vermisse ich ?

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 ; 

Der Ausgang ist die folgende:

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

Dies ist wahrscheinlich Wegthema hier, aber Ihre Definition des Typs 'tree' mir ein Rätsel. Die Blätter können einen anderen Typ als die Knoten haben? – RichouHunter

Antwort

6

In Ihrer bug Funktion gibt es diese Problematik Ausdruck:

Node(!first_ref, bug l, bug r) 

Sein Verhalten in der Reihenfolge der Auswertung der Argumente abhängt: bug l und bug r inkrementieren first_ref, so dass der Wert, der übergeben wird, möglicherweise nicht das ist, was Sie möchten.

Sie können die Reihenfolge erzwingen, indem Sie zum Beispiel tun:

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

Nur um ein wenig Kontext zu dieser Antwort hinzuzufügen. Theoretisch spielt die Reihenfolge der Auswertung in einer reinen funktionalen Sprache keine Rolle, dank der Abwesenheit von Nebenwirkungen. Natürlich bricht die Verwendung von Referenzen diese Situation, daher der lokale Bindetrick. Es ist erwähnenswert, dass die Reihenfolge der Auswertung nicht in OCaml angegeben ist, was der funktionalen Natur der Sprache entspricht. – RichouHunter

+3

@RichouHunter, OCaml ist weit davon entfernt, rein zu sein und hat viele andere Effekte neben dem veränderlichen Zustand, z.B. Ausnahmen, Nicht-Kündigung, I/O, usw. Also IMHO gibt es keine Entschuldigung dafür, keine Evaluierungsreihenfolge anzugeben. Es ist eine seiner nervigsten Fallstricke. –

+0

Ich stimme völlig zu, @AndreasRossberg. Ich frage mich, wie sich die Festlegung auf die vorhandenen Implementierungen und die Codebasis auswirken würde. – RichouHunter