Recursively iterating over an array in F #

This is a pretty basic query, but I'm having trouble with the F # syntax. I need to create a function to iterate over a two-dimensional array recursively, incrementing a counter every time a condition is met. In this case, the function takes an identifier as a parameter and then checks how many times that identifier is present inside the internal arrays. I was thinking about something like this:

let runner array count =  
    match count with  
    |0 -> (exit loop)  
    |n -> (perform function)  
        runner array count-1


For the general structure of a recursive iteration function. However, there are a few things that I'm not sure about. What are the conditions for breaking out of a recursive loop (i.e. Base case) in F #? How can I structure this so that it crosses over both the main and sub-range in order to check the ID? And how do I do this so that the counter increments with each iteration of the recursive loops, assuming I cannot use mutable functions? Any help would be appreciated.


source to share

3 answers

There is a more general way to do this. You want to dump the 2d array. They don't have a fold defined in the Array2D module, but you can write one.

let fold f state (arr: 'a [,]) =
    Seq.cast<'a> arr
    |> Seq.fold f state


We're cheating a little here, casting gives us a flat sequence of elements from the array, which we later turn into a regular fold. So now you can do:

 array |> fold (fun counter e -> if cond e then counter+1 else counter) 0


where cond is the condition you want to check.



I find 2D arrays a little awkward in F #, but here's how you could do it:

let recursivelyCountID ID (matr : 'T[,]) =
    let rec dim1 acc index0 index1 =
        if index1 > matr.GetUpperBound 1 then
            let newAcc = if matr.[index0, index1] = ID then acc+1 else acc
            dim1 newAcc index0 (index1+1)

    let rec dim0 acc index0 =
        if index0 > matr.GetUpperBound 0
        then acc
        else dim0 (acc + (dim1 0 index0 0)) (index0+1)

    dim0 (matr.GetLowerBound 0) (matr.GetLowerBound 1)


If you use an array of arrays instead, you can use functions in the standard F # module:

let IDsInInnerArray ID xs = xs |> Array.sumBy (fun i -> if i = ID then 1 else 0)
let totalIDs ID xss = xss |> (fun xs -> IDsInInnerArray ID xs) |> Array.sum


Edit: I was confused not noticing that 2D arrays implement IEnumerable, so instead of the previous code working with arrays of arrays, the direct function for 2D arrays would be:

let totalIDs ID (matr : 'T[,]) = 
    |> Seq.cast<'T> 
    |> Seq.sumBy (fun i -> if i = ID then 1 else 0)


... using the Seq.cast function, as scrwtp shows in his answer.



This is a little awkward:

 let count (x : 'a [,]) (e : 'a) =
    seq { for x in arr do yield x :?> 'a } |> Seq.where ((=) e) |> Seq.length


Arrays implement GetEnumerator, so this should work for Array3D and 4D as well.



All Articles