I’ve been dabbling with haskell in the recent days. My first semi-serious attempt to some code was inspired by a prompt at programming praxis.

What I concocted should ashame any veteran haskell programmer (if you are and have stumbled on this by accident feel free to point me in the right direction;-)):

importSystem.Environment taylor :: Double -> Double -> Double taylor x tol = reduce1 (x - fromIntegral(truncate(x / (2*pi)))*(2*pi)) tol reduce1 :: Double-> Double -> Double reduce1 x tol | x < 0 = (-reduce2 (-x) tol) | otherwise = reduce2 x tol reduce2 :: Double -> Double -> Double reduce2 x tol | x <= pi/2 = sumTaylor x tol | x <= pi = sumTaylor (pi - x) tol | x <= 3*pi/2 = (-sumTaylor (x - pi) tol) | otherwise = (-sumTaylor (2*pi - x) tol) sumTaylor :: Double -> Double -> Double sumTaylor x tol = checkdelta (scanl sumOdd (0,1,1,-1) [1..]) tolwheresumOdd (p,n,d,s) el | even el = (p-s*n/d,n*x,d*fromIntegral el,-s) | otherwise = (p,n*x,d*fromIntegral el,s) checkdelta :: [(Double,Double,Double,Double)] -> Double -> Double checkdelta (a:b:c:xs) tol =ifabs ( first4 a - first4 c ) < tolthenfirst4 celsecheckdelta (c:xs) tol checkdelta []_= 0 first4 :: (Double,Double,Double,Double) -> Double first4 (a,b,c,d) = a main :: IO () main =do(x:tol:[]) <- getArgs print $ taylor (read x) (read tol)

I was curious to see how this would fare against a C equivalent, so I quickly typed the following:

#include<stdio.h>#include<stdlib.h>#include<math.h>#definemax_iter 100/** Compute the taylor sum x-x**3/3!+x**5/5!+ ...* Stop if the difference between 2 consecutive terms is minor than a given* tolerance or if it doesn't converge after 100 iterations.**/doublesum(constdouble x,constdouble tol) { double y, old_y, num, den, sign; int i; y = 0.0; num = 1.0; den = 1.0; sign = 1.0; old_y = 100.0;for( i=1; i<max_iter; i++ ) {if(!(i & 1)) { y += sign*num/den; sign = -sign;if(fabs(y-old_y)<tol)returny; old_y = y; } num *= x; den *= (double)i; }returnNAN; } intmain(int argc, char **argv) { double x, tol, pi, y; int i, sign = 0;if(argc<2) {printf("Requires at least a numeric argument\n");return-1; } pi =atan(1.0)*4.0; x =strtod(argv[1], NULL);if(argc==3) { tol =strtod(argv[2], NULL); }else{ tol = 1.0e-9; }//Only compute for positive x (since sin(x)=-sin(-x))if( x < 0 ) { x = -x; sign = 1; }//Limit x to the 0-2pi range (sine is periodic with period=2pix -=floor(x/(2*pi)) * (2*pi);//Only compute in the first quadrant and use symmetriesif( x <= pi/2 ) { y =sum(x, tol); }elseif( x <= pi) { y =sum(pi-x, tol); }elseif( x <= 3*pi/2 ) { y = -sum(x-pi, tol); }else{ y = -sum(2*pi-x, tol); }if(sign) y = -y;printf("%.16f\n", y);return0; }

Code size wise the haskell one is indeed more compact (and I think there is still room for improvement), however the size of the executable is amazingly different. With default compiler settings for gcc and gch the C program is 8.2 kB and the haskell one 1007 KB!? I wonder what is all that overhead …

Also, performance wise the C code is about 100% faster than the haskell one (but that I expected).

Well, its not really a fair comparison, the problem is indeed more tailored to imperative programming.

#1 by maxauthority on January 19, 2010 - 15:50

Quote

You probably mean 50% faster (twice as fast). If it is 100% faster, then it would execute in 0ms while the other could take a few minutes.

#2 by bmboy on February 4, 2010 - 20:50

Quote

Thank you! I added this page to bookmark)) I think would be useful …

#3 by belavina on February 14, 2010 - 23:22

Quote

Nice article. Would be grateful for any other information concerning this topic. Thanks!