Tuesday, 6 September 2011

Haskell, where vs let in guards

I'm making a stab at learning Haskell, a functional programming language, and as part of it I'm going through the section in Seven Languages in Seven Weeks, one of the exercises is to convert a string representation of a number to a real (floating point) number, so " $2,345,678.99" to  2345678.99.
Here's my first stab :

import Char

strToNum :: String -> Float

sToN :: (Float, Int, String) -> Float
sToN (acc, dec_place, []) = acc
sToN (acc, dec_place, xs)
        | head xs == '$' = sToN (acc, dec_place, tail xs)
        | head xs == ',' = sToN (acc, dec_place, tail xs)
        | head xs == '.' = sToN (acc, 1, tail xs)
        | dec_place > 0 =
                let  newacc = acc + (fromIntegral (digitToInt (head xs)) )  / (10.0  ^ dec_place)
                in sToN (newacc, dec_place + 1, tail xs)
        | otherwise =
                let newacc = acc * 10.0 + (fromIntegral (digitToInt (head xs)) )
                in sToN (newacc, dec_place, tail xs)

strToNum "" = 0.0
strToNum(xs) = sToN (0.0, 0, xs)

It's a bit brute force and ignorance, but it works. Just thought of a more elegant solution though. The point of this post is the use of 'let' rather than 'where' in the guard expression. The guards (| expressions) handle the various characters and states of the string, and in the two that alter the accumulator define the new value via a 'let'. Initially I tried using a 'where' for this, but it seems that you can only have one per pattern sequence, it would seem that this is because 'let' is a proper expression whilst 'where' is not.

BTW I also tried this with lambdas, but fell foul of the type system.

I quite like this pattern matching approach in general, and have used it in Perl a lot, as it is quite easy to see each 'case' and the logic that goes with it.

No comments:

Post a Comment