2014-02-12 12 views
7

Betrachten Sie das folgende einfache Beispiel:Rekursion & Unveränderlichkeit in F #

type Parent = { Children : Child list } 
and Child = { Value : int ; Parent : Parent } 

let rec children = [ { Value = 0 ; Parent = parent } ] 
and parent = { Children = children } 

Der F # -Compiler intelligent genug ist richtig, diese rekursive Objekte zu initialisieren, wie

obj.ReferenceEquals(parent, parent.Children.Head.Parent) 

Jetzt läuft überprüft werden kann, prüfen die folgende Verallgemeinerung:

let length = 100 // assume arbitrary 

let rec children = List.init length (fun i -> { Value = i ; Parent = parent }) 
and parent = { Children = children } 

Diese Definition führt zu einem Compilerfehler oder. Meine Frage ist die folgende: Gibt es eine Möglichkeit, wie ich die obige Bindung machen könnte ohne Rückgriff auf Reflexion oder veränderbare Felder?

+1

Als F # Neuling wäre ich daran interessiert zu wissen, was die praktische Anwendung eines Codeausschnitts wie der oben genannten ist. –

+2

Das Beispiel selbst ist nur eine sinnlose Vereinfachung, die ich zusammen geklopft habe. Das allgemeinere Problem besteht darin, zyklische Instanzen von unveränderlichen Datenstrukturen zu initialisieren. – eirik

Antwort

15
let rec mkChild i = {Value = i; Parent = parent} 
and parent = { Children = children } 
and children = List.init length mkChild 
+2

Also, was genau sind die Grenzen? Ich konnte nichts in der Spezifikation finden. – Daniel

+0

Das ist glatt - ich habe versucht, Kinder mit Listenverständnis zu binden und das scheitert in dieser Form und der ursprünglichen Form auch. – plinth

+2

Schön gemacht! Ein großes Dankeschön an Sie, mein Herr. – eirik

0

Ich kann nicht für F# zu beantworten, aber ich bin sicher, dass es keine Möglichkeit, dass in OCaml zu tun ist, die zu F # sehr nahe ist. Dies ist das gleiche wie eine kreisförmige Liste definieren:

let rec a = 1 :: 2 :: a; 

Bei der rekursiven Objektdefinition, dann können Sie nicht Funktionsaufruf entlang des rekursiven Zyklus haben, weil es bedeutet, eine Funktion auf einem Objekt aufgerufen wird, das noch nicht ist vollständig konstruiert.

Genauer gesagt in Ihrem Beispiel, versuchen Sie (fun i -> { Value = i ; Parent = parent }) zu List.init einen Verschluss passieren, aber diese Schließung definiert werden kann nicht, da parent noch nicht vollständig definiert.