Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mutable variables in closures in F#

Tags:

f#

I am trying to port some code form java do F# that generates a grid of multidimensional points around a given point. I came up with this:

let gridGenerator midpoint stepSize steps = 
    seq {
        let n = Array.length midpoint
        let direction = Array.create n -steps
        let mutable lastIndex = n-1
        while lastIndex>=0 do
            let next = midpoint |> Array.mapi (fun i x -> x+ direction.[i]*stepSize)
            while lastIndex>=0 && direction.[lastIndex]=steps do 
                direction.[lastIndex]<- (-steps)
                lastIndex<-lastIndex-1;        
            if lastIndex>=0 then
                direction.[lastIndex]<-direction.[lastIndex]+1;
                lastIndex <- n-1;
            yield next;
    }

Apart from this code being horribly imperative (I would be grateful for hints how to fix it), I am getting a compilation error:

Program.fs(18,15): error FS0407: The mutable variable 'lastIndex' is used in an invalid way. Mutable variables cannot be captured by closures. Consider eliminating this use of mutation or using a heap-allocated mutable reference cell via 'ref' and '!'.

How can I fix this error? How can I make it more functional?

EXAMPLE: For midpoint [|0.0, 1.0|], step size 0.5 and steps 1 I expect (in any order really)

seq{[|-0.5, 0.5|], [|-0.5, 1.0|], [|-0.5, 1.5|], [|0.0, 0.5|], [|0.0, 1.0|], [|0.0, 1.5|], [|0.5, 0.5|], [|0.5, 1.0|], [|0.5, 1.5|]}

Please also note that this will be executed many times, so performance is critical.

like image 820
Grzenio Avatar asked Dec 20 '22 20:12

Grzenio


1 Answers

Here is a more functional way of doing it:

let rec gridGenerator midpoint stepSize steps =
    match midpoint with
    | [] -> Seq.singleton []
    | p::point ->
        seq {
            for d in - stepSize * steps .. stepSize .. stepSize * steps do
                for q in gridGenerator point stepSize steps do
                    yield (p + d) :: q
        }

And the signature:

val gridGenerator : int list -> int -> int -> seq<int list>

If you reuse the result, remember to cache it or convert it (to an array or a list).

like image 120
Ramon Snir Avatar answered Jan 05 '23 15:01

Ramon Snir