Search code examples
recursionf#closuresmutable

F# : Writing a function that builds a list of tuples recursively and change a mutable variable


This question is related to this previous thread.

I followed Tomas's suggestion using this piece code, and all works fine:

let GetSameColorNeighs (grid:Option<Ball>[,], row, col, color:Color) =
  let rec loop (row, col) = seq {
    if not (row < 0 || col < 0 || row > MaxLineNumber - 1 
                    || col > BallsPerLine - 1) then
        let ball = grid.[row,col]
        match ball with 
        | Some(ball) -> 
          if (!ball.visited = false || not <| ball.color.Equals(color)) then
            // Not sure what you want here - yield items using 'yield'?
            // [row , col] 
          else
            ball.visited := true
            yield row, col                 // Add single item to results
            yield! loop(row + 1, col + 1)  // Add all generated to results
            yield! loop(row - 1, col - 1)  //        -- || --
        | None  -> () }
  loop(row, col) |> Seq.toList

The code above iterate through an array 2d of "balls" and return a list of index of adjacent balls with the same color.

Now I have to modify the function in way that it returns also a boolean indicating if at least one ball of the list satisfy a certain condition. I changed the code this way but seems that I can't assign a mutable value inside that code:

let GetSameColorNeighs (grid:Option<Ball>[,], row, col, color:Color)  : List<int * int> * bool =
    let mutable b : bool = false
    let rec loop (row, col) = seq {
        if not (row < 0 || col < 0 || row > MaxLineNumber - 1 
                        || col > BallsPerLine - 1) then
            let ball = grid.[row,col]
            match ball with 
            | Some(ball) -> 
              if (ball.visited = true || not <| ball.color.Equals(color)) then
                ()
              else
                //HERE's THE PROBLEM
                if (ball_satisfy_a_certain_condition) then
                      b <- true
                ball.visited := true
                yield row, col                 // Add single item to results
                yield! loop(row + 1, col + 1)  // Add all generated to results
                yield! loop(row - 1, col - 1)  //        -- || --
            | None  -> () }
      loop(row, col) |> Seq.toList, b

It seems that a mutable variable can't be acquired by a closure (I don't know what it means).

So I have 2 questions:

  1. why is the above assignment to a mutable variable wrong?
  2. How should I refactor my code to achieve this goal?

Solution

  • In short, you have to use ref variables instead of mutable variables.

    While mutable variables are allocated on the stack, ref variables are heap-based. After each time your loop function is invoked, mutable values are wiped out when ref values are still there. Therefore, only ref values are valid to return in GetSameColorNeighs.

    This question has been asked many times here. See The mutable variable 'i' is used in an invalid way.? and this blog post for more in-depth discussion.