Reputation: 143
I have written the following code to generate all possible combinations of some numbers:
let allCombinations (counts:int[]) =
let currentPositions = Array.create (counts.Length) 0
let idx = ref (counts.Length-1)
seq{
while currentPositions.[0]<counts.[0] do
yield currentPositions
currentPositions.[!idx]<-currentPositions.[!idx]+1
while currentPositions.[!idx] >= counts.[!idx] && !idx>=1 do
currentPositions.[!idx]<-0
idx:=!idx-1
currentPositions.[!idx]<-currentPositions.[!idx]+1
idx:=counts.Length-1
}
I am consuming the sequence in some other part of the program like this:
allCombinations counts |> Seq.map (fun idx -> buildGuess n digitsPerPos idx) ...
So far so good. The programs runs as expected and generates the combinations. For input [|2;2;2|] it generates the eight values:
[|0; 0; 0|]
[|0; 0; 1|]
[|0; 1; 0|]
[|0; 1; 1|]
[|1; 0; 0|]
[|1; 0; 1|]
[|1; 1; 0|]
[|1; 1; 1|]
However when I use PSeq to parallelise the generated sequence all values to be consumed change to [|2;0;0|] which is the last value of the currentPositions array in the while loop above.
If i use
yield (currentPositions|>Array.copy)
instead of
yield currentPositions
everything works ok in both sequential and parallel versions.
Why does this happen; Is there a most efficient way to yield the result; Thank you in advance;
Upvotes: 2
Views: 215
Reputation: 1502036
The problem is that you're creating a single array which you're mutating between iterations.
You can take the parallelism out of the equation by building a list of the results instead of printing them out one by one - if you build the list first and then print them all, you'll see the same result; the list will contain the same reference 8 times, always to the same array instance.
Basically to avoid side-effects, you need each result to be independent of the other - so you should create a separate array each time.
Upvotes: 3