hoonzis
hoonzis

Reputation: 1737

F# applying function to it's result n-times

I am trying to find a functional correct way for the following piece of code:

let mutable u = initialize cities pms
for i in 0 .. 10 do
    u <- randomIteration u pms distances

randomIteration is a simple function which takes an array with 2 more parameters and returns a modified array. This process has to be repeated n-times (10 here).

I came up with a solution, which uses fold, but I am creating a "dummy" sequence just to be able to fold on it, which does not seem right.

let result = Seq.init 10 (fun i -> i) |> Seq.fold (fun uNext i -> randomIteration uNext pms distances) u

I could also use recursion with a counter variable, but that as well seems ackward. Am I just missing a simple right solution?

Upvotes: 5

Views: 3806

Answers (4)

codybartfast
codybartfast

Reputation: 7543

This is just a slight variation on Nikon-the-Third's answer. One could create a more general function that repeatedly applies another function:

let repeat n fn = List.init n (fun _ -> fn) |> List.reduce (>>)

This could then be used to create a named function that repeats the first 10 times:

let getNext10Times = repeat 10 (fun u -> randomIteration u pms distances)
getNext10Times u

or it could be used anonymously:

u |> repeat 10 (fun u -> randomIteration u pms distances)

Upvotes: 2

Richard
Richard

Reputation: 109005

I could also use recursion with a counter variable, but that as well seems ackward.

That would seem natural to me: allowing the result of one call to be passed to the next without mutable state. Something like:

let interateSelf func initial count =
  let rec inner intermediate n =
    if n = 1 then
      func intermediate
    else
      inner (func intermediate) (n - 1)
  inner initial count

Upvotes: 5

Nikon the Third
Nikon the Third

Reputation: 2831

Just trying to think outside the box here, but instead of folding over randomIteration with different arguments each time, you could create a chain of N randomIteration calls and call this chain once:

let repeat n =
    Seq.init n (fun _ u -> randomIteration u pms distances)
    |> Seq.reduce (>>)

initialize cities pms
|> repeat 10
|> printfn "Result: %A"

Upvotes: 6

N_A
N_A

Reputation: 19897

One easy change to make that less awkward is to use a sequence expression rather than Seq.init.

let result = {1..10} 
             |> Seq.fold (fun uNext i -> randomIteration uNext pms distances) u

If you really want to keep the Seq.init you could replace the identify function with the build-in one like this:

let result = Seq.init 10 id
             |> Seq.fold (fun uNext i -> randomIteration uNext pms distances) u

An alternative would be to create a recursive function something like this:

let result = let rec loop x i =
               match i with
               | 0 -> x
               | i -> loop (randomIteration x pms) (i-1)
             loop u 10

Upvotes: 5

Related Questions