Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a recursive data structure value in (functional) F#?

How can a value of type:

type Tree =
    | Node of int * Tree list

have a value that references itself generated in a functional way?

The resulting value should be equal to x in the following Python code, for a suitable definition of Tree:

x = Tree()
x.tlist = [x]

Edit: Obviously more explanation is necessary. I am trying to learn F# and functional programming, so I chose to implement the cover tree which I have programmed before in other languages. The relevant thing here is that the points of each level are a subset of those of the level below. The structure conceptually goes to level -infinity.

In imperative languages a node has a list of children which includes itself. I know that this can be done imperatively in F#. And no, it doesn't create an infinite loop given the cover tree algorithm.

like image 568
Muhammad Alkarouri Avatar asked Jun 21 '10 16:06

Muhammad Alkarouri


People also ask

How do you create a recursive function?

Writing a recursive function is almost the same as reading one: Create a regular function with a base case that can be reached with its parameters. Pass arguments into the function that immediately trigger the base case. Pass the next arguments that trigger the recursive call just once.

How do you do recursion in data structures?

In recursion, a function or method has the ability to call itself to solve the problem. The process of recursion involves solving a problem by turning it into smaller varieties of itself. Recursion in stack in data structure is when functions call themselves directly or indirectly.

Which data structure is used in recursive function?

Stack. Because of its LIFO (Last In First Out) property it remembers its 'caller' so knows whom to return when the function has to return. Recursion makes use of system stack for storing the return addresses of the function calls.


2 Answers

Tomas's answer suggests two possible ways to create recursive data structures in F#. A third possibility is to take advantage of the fact that record fields support direct recursion (when used in the same assembly that the record is defined in). For instance, the following code works without any problem:

type 'a lst = Nil | NonEmpty of 'a nelst
and 'a nelst = { head : 'a; tail : 'a lst }

let rec infList = NonEmpty { head = 1; tail = infList }

Using this list type instead of the built-in one, we can make your code work:

type Tree = Node of int * Tree lst
let rec x = Node(1, NonEmpty { head = x; tail = Nil })
like image 138
kvb Avatar answered Oct 25 '22 09:10

kvb


You cannot do this directly if the recursive reference is not delayed (e.g. wrapped in a function or lazy value). I think the motivation is that there is no way to create the value with immediate references "at once", so this would be awkward from the theoretical point of view.

However, F# supports recursive values - you can use those if the recursive reference is delayed (the F# compiler will then generate some code that initializes the data structure and fills in the recursive references). The easiest way is to wrap the refernece inside a lazy value (function would work too):

type Tree = 
    | Node of int * Lazy<Tree list>

// Note you need 'let rec' here!        
let rec t = Node(0, lazy [t; t;])

Another option is to write this using mutation. Then you also need to make your data structure mutable. You can for example store ref<Tree> instead of Tree:

type Tree = 
    | Node of int * ref<Tree> list

// empty node that is used only for initializataion
let empty = Node(0, [])
// create two references that will be mutated after creation    
let a, b = ref empty, ref empty

// create a new node
let t = Node(0, [a; b])
// replace empty node with recursive reference
a := t; b := t

As James mentioned, if you're not allowed to do this, you can have some nice properties such as that any program that walks the data structure will terminate (because the data-structrue is limited and cannot be recursive). So, you'll need to be a bit more careful with recursive values :-)

like image 34
Tomas Petricek Avatar answered Oct 25 '22 10:10

Tomas Petricek