9. June 2009 23:03 by David in
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.