Reputation: 36649
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.
Upvotes: 4
Views: 1367
Reputation: 251
Now you can just use F# 4, which doesn't have such constraint.
Upvotes: 3
Reputation: 7560
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).
Upvotes: 4
Reputation: 7560
let gridGenerator midpoint stepSize steps =
seq {
let n = Array.length midpoint
let direction = Array.create n -steps
let lastIndex = ref (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
decr lastIndex
if !lastIndex >= 0 then
direction.[!lastIndex] <- direction.[!lastIndex] + 1
lastIndex := n - 1
yield next
}
?
ref
's are very good for such uses, and are not considered mutable variables (because they are not).
Upvotes: 4