Question

How is it possible to exit early / break out of / stop an array creation in F# (in this case, of Array2D.initBased)?

Remark: dic is a Dictionary<,>() whose value is an object that has a method named someMethod that takes two int parameters.

let arr = Array2D.initBased 1 1 width height (fun x y -> 
    let distinctValues = dic |> Seq.map (fun (KeyValue(k,v)) -> v.someMethod x y) |> Set.ofSeq
        match distinctValues.count with
        | dic.Count ->
            // do something
            // exit array creation here, because I do not need arr any more if v.someMethod x y produced distinct values for each dic value
        | _ -> 
            // do something else
Was it helpful?

Solution

This is a tricky question - I don't think there is any function that lets you do this easily. I think the best option is probably to define your own higher-order function (implemented using not very elegant recursion) that hides the behavior.

The idea would be to define tryInitBased that behaves as initBased but the user-provided function can return option (to indicate failure) and the function returns option (either successfully created array or None):

/// Attempts to initialize a 2D array using the specified base offsets and lengths.
/// The provided function can return 'None' to indicate a failure - if the initializer
/// fails for any of the location inside the array, the construction is stopped and
/// the function returns 'None'.
let tryInitBased base1 base2 length1 length2 f = 
  let arr = Array2D.createBased base1 base2 length1 length2 (Unchecked.defaultof<_>)
  /// Recursive function that fills a specified 'x' line
  /// (returns false as soon as any call to 'f' fails, or true)
  let rec fillY x y = 
    if y < (base2+length2) then
      match f x y with
      | Some v -> 
          arr.[x, y] <- v
          fillY x (y + 1)
      | _ -> false
    else true
  /// Recursive function that iterates over all 'x' positions
  /// and calls 'fillY' to fill individual lines
  let rec fillX x = 
    if x < (base1+length1) then 
      if fillY x base2 then fillX (x + 1)
      else false
    else true
  if fillX base1 then Some arr else None

Then you can keep your code pretty much the same, but replace initBased with tryInitBased and return None or Some(res) from the lambda function.

I also posted the function to F# snippets with a nicer formatting.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top