colinfang
colinfang

Reputation: 21767

How to make this loop more functional without bringing too much overheads

 for i in a..b do
     res <- res * myarray.[i]
 res

Do I have to use like

  Array.fold (*) 1 (Array.sub myarray a (b - a + 1))

, which I believe is rather slow and not that concise?

Upvotes: 2

Views: 169

Answers (3)

gradbot
gradbot

Reputation: 13862

If you're concerned with speed then I'd shy away from using seq unless you're prototyping. Either stick with the for loop or rewrite as a recursive function. The example you gave is simplistic and sometimes more complex problems are better represented as recursion.

let rec rangeProduct a b total (array : _[]) =
    if a <= b then
        rangeProduct (a + 1) b (total * array.[a]) array
    else
        total

let res = myArray |> rangeProduct a b res

There is no overhead here, it's as fast as possible, there is no mutation, and it's functional.

Upvotes: 4

Tomas Petricek
Tomas Petricek

Reputation: 243126

Daniel's solution is pretty neat and I think it should be nearly as efficient as the for loop, because it does not need to clone the array.

If you wanted a more concise solution, then you can use indexer instead of Array.sub, which does need to clone some part of the array, but it looks quite neat:

myarray.[a .. b] |> Seq.fold (*) 1

This clones a part of the array because the slicing operation returns an array. You could define your own slicing operation that returns the elements as seq<'T> (and thus does not clone the whole array):

module Array =
  let slice a b (arr:'T[]) = 
    seq { for i in a .. b -> arr.[i] }

Using this function, you could write:

myarray |> Array.slice a b |> Seq.fold (*) 1

I believe this more directly expresses the functionality that you're trying to implement. As always with performance - you should measure the performance to see if you need to make such optimizations or if the first version is fast enough for your purpose.

Upvotes: 4

Daniel
Daniel

Reputation: 47914

Don't know if you'll find it any better, but you could do:

Seq.fold (fun r i -> r * myarray.[i]) 1 {a .. b}

Upvotes: 5

Related Questions