Tail End Recursion and F# – Part 2

by Call me dave... 9. June 2009 17:03

In first post on Tail End Recursion it was shown that recursive functions can be automatically replaced with a fast while loop by the compiler.  The post implies that the head::tail syntax should be used to write F# code instead of using a while loop.  In reality this is often unnecessary due to the pipeline operator |> .  The pipeline operator is used to channel the output of one function into the input parameters of another.  This is similar to a pipe operator in Unix.

ls | grep “my program” | wc –l

In F# the operator allows streams to be filtered and enriched.  I have converted the previous code to use the pipeline operator.  The code function coverts a (x,y) tuple into a (x, y, x*x, y*y, x*y) tuple this holds the temporary working data for the program.  The work data is then summed together and finally the linear regression is calculated.

   1: let public regressionUsingPipelineOperator l : linearRegressionCoeffs =
   2:   let length = (float) (List.length l)
   3:  
   4:   let working s = 
   5:     let (x,y) = s
   6:     (x, y , x*x, y*y, x*y)
   7:  
   8:   let calc =
   9:     l |> Seq.map working
  10:       |> Seq.fold (fun s acc ->                    
  11:                     let (a,b,c,d,e) = s
  12:                     let (a1,b1,c1,d1,e1) = acc
  13:                     (a + a1, b + b1, c + c1, d + d1, e + e1)
  14:                  ) (0.0, 0.0, 0.0, 0.0, 0.0)          
  15:       |> fun (sumX, sumY, sumXX, sumYY, sumXY)   ->
  16:            let meanX = sumX/length
  17:            let meanY = sumY/length
  18:            let m = (sumXY - length*meanX*meanY) / (sumXX - length* meanX * meanX)
  19:            let c = meanY - m*meanX
  20:            {m = m; c=c;}
  21:   
  22:   calc

In the example above the pipeline method is slightly slower than the hand crafted code using the head::tail syntax (since in a single step the code does the equivalent of the map and fold operation).  However the programmer’s intent is far clearer and hence should be used as the default solution.

Update:

The code above can be simplified to by reversing the parameters to the Seq.Fold method as follows.

   1: let public regressionUsingPipelineOperator l : linearRegressionCoeffs =
   2:   let length = (float) (List.length l)
   3:  
   4:   let calc =
   5:     l|> Seq.fold (fun acc (x,y) ->                    
   6:                     let (a,b,c,d,e) = acc
   7:                     (a + x, b + y, c + x*x, d + y*y, e + x*y)
   8:                  ) (0.0, 0.0, 0.0, 0.0, 0.0)          
   9:       |> fun (sumX, sumY, sumXX, sumYY, sumXY)   ->
  10:            let meanX = sumX/length
  11:            let meanY = sumY/length
  12:            let m = (sumXY - length*meanX*meanY) / (sumXX - length* meanX * meanX)
  13:            let c = meanY - m*meanX
  14:            {m = m; c=c;}
  15:   
  16:   calc

Whoops…  F# code seems to continually shrink.

Currently rated 4.7 by 3 people

  • Currently 4.666667/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Comments are closed

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

Who is Dave

David Ross - Gold Coast QLD

A QLD'er living in Sydney

Email:willmation(at)gmail.com