Can Haskell optimize function calls the same way Clang / GCC does? - c++

I want to ask you if Haskell and C++ compilers can optimize function calls the same way.
Please look at following codes. In the following example Haskell is significantly faster than C++.
I have heard that Haskell can compile to LLVM and can be optimized by the LLVM passes. Additionally I have heard that Haskell has some heavy optimizations under the hood.
But the following examples should be able to work with the same performance.
I want to ask:
Why my sample benchmark in C++ is slower than the on in Haskell?
is it possible to further optimize the codes?
(I'm using LLVM-3.2 and GHC-7.6).
C++ code:
#include <cstdio>
#include <cstdlib>
int b(const int x){
return x+5;
}
int c(const int x){
return b(x)+1;
}
int d(const int x){
return b(x)-1;
}
int a(const int x){
return c(x) + d(x);
}
int main(int argc, char* argv[]){
printf("Starting...\n");
long int iternum = atol(argv[1]);
long long int out = 0;
for(long int i=1; i<=iternum;i++){
out += a(iternum-i);
}
printf("%lld\n",out);
printf("Done.\n");
}
compiled with clang++ -O3 main.cpp
haskell code:
module Main where
import qualified Data.Vector as V
import System.Environment
b :: Int -> Int
b x = x + 5
c x = b x + 1
d x = b x - 1
a x = c x + d x
main = do
putStrLn "Starting..."
args <- getArgs
let iternum = read (head args) :: Int in do
putStrLn $ show $ V.foldl' (+) 0 $ V.map (\i -> a (iternum-i))
$ V.enumFromTo 1 iternum
putStrLn "Done."
compiled with ghc -O3 --make -fforce-recomp -fllvm ghc-test.hs
speed results:
Running testcase for program 'cpp/a.out'
-------------------
cpp/a.out 100000000 0.0% avg time: 105.05 ms
cpp/a.out 200000000 11.11% avg time: 207.49 ms
cpp/a.out 300000000 22.22% avg time: 309.22 ms
cpp/a.out 400000000 33.33% avg time: 411.7 ms
cpp/a.out 500000000 44.44% avg time: 514.07 ms
cpp/a.out 600000000 55.56% avg time: 616.7 ms
cpp/a.out 700000000 66.67% avg time: 718.69 ms
cpp/a.out 800000000 77.78% avg time: 821.32 ms
cpp/a.out 900000000 88.89% avg time: 923.18 ms
cpp/a.out 1000000000 100.0% avg time: 1025.43 ms
Running testcase for program 'hs/main'
-------------------
hs/main 100000000 0.0% avg time: 70.97 ms (diff: 34.08)
hs/main 200000000 11.11% avg time: 138.95 ms (diff: 68.54)
hs/main 300000000 22.22% avg time: 206.3 ms (diff: 102.92)
hs/main 400000000 33.33% avg time: 274.31 ms (diff: 137.39)
hs/main 500000000 44.44% avg time: 342.34 ms (diff: 171.73)
hs/main 600000000 55.56% avg time: 410.65 ms (diff: 206.05)
hs/main 700000000 66.67% avg time: 478.25 ms (diff: 240.44)
hs/main 800000000 77.78% avg time: 546.39 ms (diff: 274.93)
hs/main 900000000 88.89% avg time: 614.12 ms (diff: 309.06)
hs/main 1000000000 100.0% avg time: 682.32 ms (diff: 343.11)
EDIT
Of course we cannot compare speed of languages, but the speed of implementiations.
But I'm curious if Ghc and C++ compilers can optimize function calls the same way
I've edited the question with new benchmark and codes based on your help :)

If your goal is to get this running as quickly as your C++ compiler, then you
would want to use a data structure that the compiler can have its way with.
module Main where
import qualified Data.Vector as V
b :: Int -> Int
b x = x + 5
c x = b x + 1
d x = b x - 1
a x = c x + d x
main = do
putStrLn "Starting..."
putStrLn $ show $ V.foldl' (+) 0 $ V.map a $ V.enumFromTo 1 100000000
putStrLn "Done."
GHC is able to completely eliminate the loop and just inserts a constant into
the resulting assembly. On my computer, this now has a runtime of < 0.002s, when
using the same optimization flags as you originally specified.
As a follow up based on the comments by #Yuras, the core produced by the
vector based solution and the stream-fusion solution are functionally
identical.
Vector
main_$s$wfoldlM'_loop [Occ=LoopBreaker]
:: Int# -> Int# -> Int#
main_$s$wfoldlM'_loop =
\ (sc_s2hW :: Int#) (sc1_s2hX :: Int#) ->
case <=# sc1_s2hX 100000000 of _ {
False -> sc_s2hW;
True ->
main_$s$wfoldlM'_loop
(+#
sc_s2hW
(+#
(+# (+# sc1_s2hX 5) 1)
(-# (+# sc1_s2hX 5) 1)))
(+# sc1_s2hX 1)
}
stream-fusion
$wloop_foldl [Occ=LoopBreaker]
:: Int# -> Int# -> Int#
$wloop_foldl =
\ (ww_s1Rm :: Int#) (ww1_s1Rs :: Int#) ->
case ># ww1_s1Rs 100000000 of _ {
False ->
$wloop_foldl
(+#
ww_s1Rm
(+#
(+# (+# ww1_s1Rs 5) 1)
(-# (+# ww1_s1Rs 5) 1)))
(+# ww1_s1Rs 1);
True -> ww_s1Rm
}
The only real difference is the choice of comparison operation for the
termination condition. Both versions compile to tight tail recursive loops
that can be easily optimized by LLVM.

ghc doesn't fuse lists (avoiding success at all costs?)
Here is version that uses stream-fusion package:
module Main where
import Prelude hiding (map, foldl)
import Data.List.Stream
import Data.Stream (enumFromToInt, unstream)
import Text.Printf
import Control.Exception
import System.CPUTime
b :: Int -> Int
b x = x + 5
c x = b x + 1
d x = b x - 1
a x = c x + d x
main = do
putStrLn "Starting..."
putStrLn $ show $ foldl (+) 0 $ map (\z -> a z) $ unstream $ enumFromToInt 1 100000000
putStrLn "Done."
I don't have llvm installed to compare with your results, but it is 10x faster then your version (compiled without llvm).
I think vector fusion should perform even faster.

As others have pointed out, you're not comparing equivalent algorithms. As Yuras pointed out GHC doesn't fuse lists. Your Haskell version will actually allocate that entire list, it will be done lazily one cell at a time, but it will be done. Below is a version that's algorithmically closer to your C version. On my system it runs in the same time as the C version.
{-# LANGUAGE BangPatterns #-}
module Main where
import Text.Printf
import Control.Exception
import System.CPUTime
import Data.List
a,b,c :: Int -> Int
b x = x + 5
c x = b x + 1
d x = b x - 1
a !x = c x + d x
-- Don't allocate a list, iterate and increment as the C version does.
applyTo !acc !n
| n > 100000000 = acc
| otherwise = applyTo (acc + a n) (n + 1)
main = do
putStrLn "Starting..."
print $ applyTo 0 1
putStrLn "Done."
Comparing it with time:
ghc -O3 bench.hs -fllvm -fforce-recomp -o bench-hs && time ./bench-hs
[1 of 1] Compiling Main ( bench.hs, bench.o )
Linking bench-hs ...
Starting...
10000001100000000
Done.
./bench-hs 0.00s user 0.00s system 0% cpu 0.003 total
Compared to C:
clang++ -O3 bench.cpp -o bench && time ./bench
Starting...
10000001100000000
Done.
./bench 0.00s user 0.00s system 0% cpu 0.004 total

Related

Unhandled Exception with OCaml 5.0.0~beta1

I'm inconsistently getting this error in a first experiment with OCaml 5.0.0~beta1:
Fatal error: exception Stdlib.Effect.Unhandled(Domainslib__Task.Wait(_, _))
My setup:
Processor: Intel(R) Core(TM) i7-8750H CPU # 2.20GHz
Debian 10 (buster)
opam version 2.1.3 installed as binary from this script
opam switch: "→ 5.0.0~beta1 ocaml-base-compiler.5.0.0~beta1 5.0.0~beta1"
After a quick read of this tutorial, I copied the parallel_matrix_multiply function and added some code in the end just to use it:
open Domainslib
let parallel_matrix_multiply pool a b =
let i_n = Array.length a in
let j_n = Array.length b.(0) in
let k_n = Array.length b in
let res = Array.make_matrix i_n j_n 0 in
Task.parallel_for pool ~start:0 ~finish:(i_n - 1) ~body:(fun i ->
for j = 0 to j_n - 1 do
for k = 0 to k_n - 1 do
res.(i).(j) <- res.(i).(j) + a.(i).(k) * b.(k).(j)
done
done);
res ;;
let pool = Task.setup_pool ~num_domains:3 () in
let a = Array.make_matrix 2 2 1 in
let b = Array.make_matrix 2 2 2 in
let c = parallel_matrix_multiply pool a b in
for i = 0 to 1 do
for j = 0 to 1 do
Printf.printf "%d " c.(i).(j)
done;
print_char '\n'
done;;
I then compile it with no errors with
ocamlfind ocamlopt -linkpkg -package domainslib parallel_for.ml
and then comes the problem: executing the generated a.out file sometimes (rarely) prints the expected output
4 4
4 4
but usually ends with the error mentioned earlier:
Fatal error: exception Stdlib.Effect.Unhandled(Domainslib__Task.Wait(_, _))
Sorry if I am making some trivial mistake, but I can't understand what is going on, especially given that the error happens inconsistently.
The parallel_matrix_multiply computation is running outside of the Domainslib scheduler, thus whenever a task yields to the scheduler, the Wait effect is unhandled and transformed into a Effect.Unhandled exception.
The solution is to run the parallel computation within Task.run:
...
let c = Task.run pool (fun () -> parallel_matrix_multiply pool a b) in
...

Replacement for chained if-statements

I'm new to SML and I'm at the point where I can write functional code but I'm unsure of whether there's a more proper or idiomatic way to do things. SML only allows value constructors in patterns, so a case statement doesn't work below. SML also doesn't allow multiple else-if statements.
The following works, but has an ugly triply-nested for-loop. Is there a more idiomatic way to write the following code?
datatype coins = penny | nickle | dime | quarter;
fun valueToCoins 0 = nil
| valueToCoins x =
if x >= 25
then quarter::valueToCoins(x-25)
else
if x >= 10
then dime::valueToCoins(x-10)
else
if x >= 5
then nickle::valueToCoins(x-5)
else penny::valueToCoins(x-1);
The comments have addressed this, but really you've done the right thing. You just need to format it properly and it looks reasonable.
datatype coins = penny | nickle | dime | quarter;
fun valueToCoins 0 = nil
| valueToCoins x =
if x >= 25 then
quarter :: valueToCoins(x - 25)
else if x >= 10 then
dime :: valueToCoins(x - 10)
else if x >= 5 then
nickle :: valueToCoins(x - 5)
else
penny :: valueToCoins(x - 1);

How to efficiently read a line of integers in ocaml

I'd like to efficiently read a large line (~4000 characters) from stdin. I'll have to read ~4000 lines as well.
The line is formatted as follows:
INTEGERwhitespaceINTEGERwhitespace....
For example, 100 33 22 19 485 3601...
Afterwards, the data needs to be processed, so the initial solution I used with read_line() |> String.split_on_char ' ' |> ... was too slow O(3n).
I want to use something like Scanf:
bscanf ic "%d" int_of_string
But I'm not sure how to account for the whitespaces, or if it's fast enough. Are there any solutions for this?
I created a file with 10000 lines of 4000 random integers.
I then wrote these 4 main functions (read_int is an auxiliary one) that have the same output:
let read_int ic =
let rec aux acc =
match input_char ic with
| ' ' | '\n' -> acc
| c -> aux ((10 * acc) + (Char.code c - 48))
in
aux 0
let read_test_int () =
let ic = open_in "test" in
let max = ref 0 in
try
while true do
read_int ic |> fun e -> if e > !max then max := e
done
with End_of_file ->
close_in ic;
Format.eprintf "%d#." !max
let read_test_line () =
let ic = open_in "test" in
let max = ref 0 in
try
while true do
input_line ic |> String.split_on_char ' '
|> List.iter (fun e ->
let e = int_of_string e in
if e > !max then max := e)
done
with End_of_file ->
close_in ic;
Format.eprintf "%d#." !max
let read_test_line_map () =
let ic = open_in "test" in
let max = ref 0 in
try
while true do
input_line ic |> String.split_on_char ' ' |> List.map int_of_string
|> List.iter (fun e -> if e > !max then max := e)
done
with End_of_file ->
close_in ic;
Format.eprintf "%d#." !max
let read_test_scanf () =
let ic = Scanf.Scanning.open_in "test" in
let max = ref 0 in
try
while true do
Scanf.bscanf ic "%d " (fun i -> i) |> fun e -> if e > !max then max := e
done
with End_of_file ->
Scanf.Scanning.close_in ic;
Format.eprintf "%d#." !max
read_test_int creates an integer by reading characters one by one
read_test_line is your initial solution
read_test_line_map is your initial solution with a mapping from string to int
read_test_scanf is the solution you'd like to test
I then tested the four of them with hyperfine and here are the outputs:
hyperfine --warmup 3 -P arg 1 4 'dune exec program -- {arg}'
read_int
Benchmark #1: dune exec program -- 1
Time (mean ± σ): 1.509 s ± 0.072 s [User: 1.460 s, System: 0.049 s]
Range (min … max): 1.436 s … 1.618 s 10 runs
read_line
Benchmark #2: dune exec program -- 2
Time (mean ± σ): 1.818 s ± 0.016 s [User: 1.717 s, System: 0.100 s]
Range (min … max): 1.794 s … 1.853 s 10 runs
read_line_map
Benchmark #4: dune exec program -- 4
Time (mean ± σ): 2.158 s ± 0.127 s [User: 2.108 s, System: 0.050 s]
Range (min … max): 2.054 s … 2.482 s 10 runs
read_scanf
Benchmark #3: dune exec program -- 3
Time (mean ± σ): 5.017 s ± 0.103 s [User: 4.957 s, System: 0.060 s]
Range (min … max): 4.893 s … 5.199 s 10 runs
It looks like my own implementation of read_int is the better one and input_line is just slightly worse since you first create a string then go through it once to split it then go through the list to read the integers. scanf is sadly always the worst. The difference starts to be visible with these kind of values (10000 lines, 4000 integers), for 4000 lines of 4000 characters I couldn't find any real difference.
Hyperline gives the following summary:
Summary
'dune exec program -- 1' ran
1.20 ± 0.06 times faster than 'dune exec program -- 2'
1.43 ± 0.11 times faster than 'dune exec program -- 4'
3.33 ± 0.17 times faster than 'dune exec program -- 3'
[EDIT]
I created two new benchs using OCamllex:
lexer.mll
let digit = ['0'-'9']
rule integers = parse
| ' ' | '\n' { integers lexbuf }
| digit+ as inum { int_of_string inum }
| _ { failwith "not a digit or a space" }
| eof { raise End_of_file }
and
lexer_list.mll
{ let l = ref [] }
let digit = ['0'-'9']
rule integers = parse
| ' ' | '\n' { integers lexbuf }
| digit+ as inum { l := int_of_string inum :: !l; integers lexbuf }
| _ { failwith "not a digit or a space" }
| eof { !l }
Rerunning the benchmarks here are the results:
❯ hyperfine --warmup 3 -P arg 1 6 'dune exec program -- {arg}'
Benchmark #1: dune exec program -- 1
Time (mean ± σ): 1.394 s ± 0.044 s [User: 1.358 s, System: 0.036 s]
Range (min … max): 1.360 s … 1.483 s 10 runs
Benchmark #2: dune exec program -- 2
Time (mean ± σ): 1.674 s ± 0.011 s [User: 1.590 s, System: 0.084 s]
Range (min … max): 1.657 s … 1.692 s 10 runs
Benchmark #3: dune exec program -- 3
Time (mean ± σ): 4.886 s ± 0.304 s [User: 4.847 s, System: 0.037 s]
Range (min … max): 4.627 s … 5.460 s 10 runs
Benchmark #4: dune exec program -- 4
Time (mean ± σ): 1.949 s ± 0.023 s [User: 1.908 s, System: 0.041 s]
Range (min … max): 1.925 s … 1.984 s 10 runs
Benchmark #5: dune exec program -- 5
Time (mean ± σ): 2.824 s ± 0.013 s [User: 2.784 s, System: 0.039 s]
Range (min … max): 2.798 s … 2.843 s 10 runs
Benchmark #6: dune exec program -- 6
Time (mean ± σ): 5.832 s ± 0.074 s [User: 5.493 s, System: 0.333 s]
Range (min … max): 5.742 s … 5.981 s 10 runs
Summary
'dune exec program -- 1' ran
1.20 ± 0.04 times faster than 'dune exec program -- 2'
1.40 ± 0.05 times faster than 'dune exec program -- 4'
2.03 ± 0.07 times faster than 'dune exec program -- 5'
3.51 ± 0.24 times faster than 'dune exec program -- 3'
4.18 ± 0.14 times faster than 'dune exec program -- 6'
Creating a list before iterating over it is the worst possible solution (even worse than scanf, imagine!) but lexing is not that bad (but not that good either)
So, to summarise, the solutions from best to worst are:
custom read int
read line
lexing int by int
read line with a mapping
scanf
lexing the whole file to a list of int
[Benching with memtrace]
This made me realise something, by the way, in case you ever read this:
if you're trying to bench your solutions, never have memtrace in your code. I was trying something and had Memtrace.trace_if_requested (); at the start of my entry point. Well, it just messes with everything and the benchs were completely wrong:
❯ hyperfine --warmup 3 -P arg 1 6 'dune exec program -- {arg}'
Benchmark #1: dune exec program -- 1
Time (mean ± σ): 7.003 s ± 0.201 s [User: 6.959 s, System: 0.043 s]
Range (min … max): 6.833 s … 7.420 s 10 runs
Benchmark #2: dune exec program -- 2
Time (mean ± σ): 1.801 s ± 0.060 s [User: 1.697 s, System: 0.104 s]
Range (min … max): 1.729 s … 1.883 s 10 runs
Benchmark #3: dune exec program -- 3
Time (mean ± σ): 4.817 s ± 0.120 s [User: 4.757 s, System: 0.058 s]
Range (min … max): 4.679 s … 5.068 s 10 runs
Benchmark #4: dune exec program -- 4
Time (mean ± σ): 2.028 s ± 0.023 s [User: 1.994 s, System: 0.032 s]
Range (min … max): 1.993 s … 2.071 s 10 runs
Benchmark #5: dune exec program -- 5
Time (mean ± σ): 2.997 s ± 0.108 s [User: 2.948 s, System: 0.046 s]
Range (min … max): 2.889 s … 3.191 s 10 runs
Benchmark #6: dune exec program -- 6
Time (mean ± σ): 6.109 s ± 0.161 s [User: 5.753 s, System: 0.349 s]
Range (min … max): 5.859 s … 6.322 s 10 runs
Summary
'dune exec program -- 2' ran
1.13 ± 0.04 times faster than 'dune exec program -- 4'
1.66 ± 0.08 times faster than 'dune exec program -- 5'
2.67 ± 0.11 times faster than 'dune exec program -- 3'
3.39 ± 0.14 times faster than 'dune exec program -- 6'
3.89 ± 0.17 times faster than 'dune exec program -- 1'
My understanding is that memtrace is able to do a lot of work on my custom solution since the whole code is directly available whereas for the rest it can just scratch the surface (I may be completely wrong but it took me some time to figure out that memtrace was spoiling my benchmarks)
[Following #ivg's comment]
lexer_parser.mll
{
open Parser
}
let digit = ['0'-'9']
rule integers = parse
| ' ' | '\n' { integers lexbuf }
| digit+ as inum { INT (int_of_string inum) }
| _ { failwith "not a digit or a space" }
| eof { raise End_of_file }
and parser.mly
%token <int> INT
%start main /* the entry point */
%type <int> main
%%
main:
| INT { $1 }
;
and in main.ml
let read_test_lexer_parser () =
let ic = open_in "test" in
let lexbuf = Lexing.from_channel ic in
let max = ref 0 in
try
while true do
let result = Parser.main Lexer_parser.integers lexbuf in
if result > !max then max := result
done
with End_of_file ->
close_in ic;
Format.eprintf "%d#." !max
(I cut some benchs)
❯ hyperfine --warmup 3 -P arg 1 7 'dune exec program -- {arg}'
Benchmark #1: dune exec program -- 1
Time (mean ± σ): 1.357 s ± 0.030 s [User: 1.316 s, System: 0.041 s]
Range (min … max): 1.333 s … 1.431 s 10 runs
Benchmark #6: dune exec program -- 6
Time (mean ± σ): 5.745 s ± 0.289 s [User: 5.230 s, System: 0.513 s]
Range (min … max): 5.549 s … 6.374 s 10 runs
Benchmark #7: dune exec program -- 7
Time (mean ± σ): 7.195 s ± 0.049 s [User: 7.161 s, System: 0.034 s]
Range (min … max): 7.148 s … 7.300 s 10 runs
Summary
'dune exec program -- 1' ran
4.23 ± 0.23 times faster than 'dune exec program -- 6'
5.30 ± 0.12 times faster than 'dune exec program -- 7'
I may have not done it properly hence the poor result but this doesn't seem promising. The way I'm doing it is that I want to get the value as soon as it's read to handle it otherwise I'll have to create a list of values and this will be even worse (believe me, I tried, it took 30 seconds to find the max value).
My dune file, in case you're wondering, looks like this (I have an empty program.opam file to please dune):
(executable
(name main)
(public_name program)
)
(ocamllex lexer)
(ocamllex lexer_list)
(ocamllex lexer_parser)
(ocamlyacc parser)

output message overlapped during cmake concurrent build

I'm trying to make build-system runs unit-test automatically.
The problem is that test output was duplicated when i use -j option.
have possible solution...?
Parallel build + consistent output
similar to
function()
synchronized {
add_custom_command("...run test...")
}
endfunction()
which is no cmake syntax
src - https://github.com/dearblanc/cmake_practice
< first build >
< second build > - make failure by intention
< wow >
Running module_a_test
Running module_b_test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set[-=u=p=.=
=[=-=-=-=-=-]- -R-u-n-n]i n1g t2e stte sftrso mf rMoOmD U2L Et_eAs
t[ sRuUiNt e s .
[ -]- -M-O-D-U-L-E-_-A]. GGElTo
bCa:l\ Utseesrts \ednevairrbo\nvmsecnotd es_ewto-rukps.p
a[c-e-\-c-m-a-k-e-_-p-r]a c1t itcee\smto dfurloem_ aM\OsDrUcL\Em_oBd2u
l[e _RaU_Nt e s t . c c]: 7M:O DFUaLiEl_uBr2e.G
EETx
p[e c t e d e qOuKa l]i tMyO DoUfL Et_hBe2s.eG EvTa l(u0e sm:s
)
["-A-A-"-
- - -a-.-g-e]t (1)
t e s t Wfhriocmh MiOsD:U L"EA_"B
2 ([8 mFsA ItLoEtDal)
][ -M-O-D-U-L-E-_-A-.-G]E T1 (t4e5s tm sf)r
o[m- -M-O-D-U-L-E-_-B-1]
[1 RtUeNs t f r o m] MMOODDUULLEE__AB 1(.1G0E1T
mCs: \tUostearls)\
d
e[a-r-b-\-v-s-c-o-d-e-_]w oGrlkosbpaalc et\ecsmta keen_vpirraocntmiecnet\ mtoedaurl-ed_obw\ns
r[c=\=m=o=d=u=l=e=_=b=1]_ t1e stte.sctc :f6r:o mF a1i ltuerset
Esxupietcet erda ne.q u(a1l5i5t ym so ft otthaels)e
[v a lPuAeSsS:E
D "]B B01 "t
e s tbs1..
g[e t (F)A
I L E D W h]i c1h tiess:t ," Bl1"i
s[t e dF AbIeLlEoDw :
][ M OFDAUILLEE_DB 1 .]G ETM O(D3U9L Em_sA).
[G-E-T-
-
- -1- -F-A-I]L E1D tTeEsStT
from MODULE_B1 (96 ms total)

How do I print out lines recursively from a text file along with the average value of total elements from per line?

I know this question sounds a bit challenging, but hey I find nothing like this relates with F sharp here. Okay so, since in my previous question I mentioned I'm new to F#. Thanks to several of fellow programmers' helps here to solve my sum function in my previous question. So, I have a text file that contained more than 20 lines and I want to print out lines with year and average of total elements from each year and its elements.
Sample text lines
2009 1.3 3.51 6.76 5.80 4.48 5.47 2.06 4.3 0.54 7.69 1.27 2.9
2008 3.53 3.71 1.88 2.46 4.63 4.88 4.53 1.51 10.83 2.7 1.28 6.51
2007 2.88 2.19 3.55 3.95 2 3.1 4.18 8.76 1.91 2.01 1.67 3.54
2006 3.48 1.33 3.16 3.87 3.19 3.87 4.24 7.12 4.32 6.63 2.97 3.37
2005 5.32 2.41 1.76 1.63 1.78 1.07 2.07 1.44 2.68 1.14 2.15 1.38
2004 1.09 0.75 3.93 1.9 5.57 2.94 4.46 5.01 0.86 2.42 5.02 1.75
....
Now, I have a couple functions to show you. Unfortunately, my print function only prints out the first line.
let rec print year values =
if values = [] then
()
else
printfn ""
printfn "%A: %A" year values
and the 2nd function which does the sum of elements perfectly, but I cannot manage to get it to divide it by 12 elements properly.
let sum (values: double list) =
let rec sum values accum =
match values with
| [] -> accum
| head :: tail -> sum tail (accum + head) / 12.0 // would 12.0 work right?
sum values 0.0
in the main
let (year, values) = ParseLine file.Head
printfn "%A: %A" print (year (sum values)) // year gets the error, according to visual studio
Thanks to John Palmer who posted a quick solution. the fixed code is
let sum (values: double list) =
let rec sum values accum =
match values with
| [] -> accum
| head :: tail -> sum tail (accum + head/12.0)
sum values 0.0