2

I am trying to do an "unfold" - (I think), by starting with an initial value, applying some function to it repeatedly, and then getting a sequence as a result.

In this example, I'm trying to start with 1.0, multiply it by .80, and do it 4 times, such that I end up with an array = [| 1.0; 0.80; 0.64; 0.512 |]

VS 2010 says I'm using "i" in an invalid way, and that mutable values cannot be captured by closures - so this function does not compile. Can anyone possibly suggest a clean approach that actually works? Thank you.

let expSeries seed fade n = 
//take see and repeatedly multiply it by the fade factor n times...
    let mutable i = 0;
    let mutable weight = seed;
    [| while(i < n) do
          yield weight;
          weight <- weight * fade |]

let testWeights = expSeries 1.0 0.80 4
fs_tech
  • 193
  • 12

4 Answers4

7
let exp_series seed fade n =  
    Array.init (n) (fun i -> seed * fade ** (float i))
nlucaroni
  • 47,556
  • 6
  • 64
  • 86
  • Thank you, this is perfect. Very concise, and I get it. Cheers. – fs_tech Mar 02 '10 at 15:34
  • 2
    Although I like the approach, this isn't very idiomatic F# (and doesn't compile on F# 1.9.9.9); more commonly one would use `*` in place of `*.` and `float` in place of `float_of_int`. – kvb Mar 02 '10 at 15:35
  • @kvb - I was thinking the same thing. – ChaosPandion Mar 02 '10 at 15:36
  • I mostly write ocaml, so those things will slip through the cracks. I have noticed a lot of divergence of F# lately, so I might be best not answering these questions anymore. :) – nlucaroni Mar 02 '10 at 15:45
  • Following @kvb's answer, I would say this i not even idiomatic functional programming. One would expect to see a definition of a sequence, and a "take n" out of that sequence. – xtofl Mar 08 '10 at 10:49
  • That would work better with lazy eval. ocaml has this, but it is much more verbose. Seq are lazy then? Either way, I disagree with you. I don't see why using the closed form Solution to the elements of a sequence isn't FP. – nlucaroni Mar 17 '10 at 14:50
2

I think this recursive version should work.

let expSeries seed fade n = 
   let rec buildSeq i weight = seq {
       if i < n then
           yield weight;
           yield! buildSeq (i + 1) (weight * fade) 
   } 
   buildSeq 0 seed
   |> Seq.toArray
ChaosPandion
  • 77,506
  • 18
  • 119
  • 157
  • Think of it like this, (*man I hope I don't make a fool of myself*) the `buildSeq` function represents the plan to build the array and `Seq.toArray` function actually builds the array from the plan. – ChaosPandion Mar 02 '10 at 15:40
  • ok - that does make sense. I'm still getting used to writing recursive functions, and the "yield!" syntax. Thanks for the explanation. – fs_tech Mar 02 '10 at 15:44
  • @fs_tech - A few months ago I would have said the same thing. – ChaosPandion Mar 02 '10 at 15:49
2

Based on the anwer to this question, you can create an unfold, and take a number of values of it:

let weighed startvalue factor  = 
  startvalue |> Seq.unfold (fun x -> Some (x, factor * x))

let fivevalues = weighed 1.0  .8   |> Seq.take 5
Community
  • 1
  • 1
xtofl
  • 40,723
  • 12
  • 105
  • 192
  • I would recommend the arguments to `weighed` not be tupled so you don't have to use the parenthesis and comma everywhere. – ChaosPandion Mar 02 '10 at 15:59
1

If you want to explicitly use an unfold, here's how:

let expSeries seed fade n =
  Seq.unfold 
    (fun (weight,k) -> 
      if k > n then None 
      else Some(weight,(weight*fade, k+1)))
    (seed,1)
  |> Array.ofSeq

let arr = expSeries 1.0 0.80 4

Note that the reason your original code won't work is that mutable bindings can't be captured by closures, and sequence, list, and array expressions implicitly use closures.

kvb
  • 54,864
  • 2
  • 91
  • 133