Just to try Haskell, I wrote a program that takes an integer between 1 and 999 and reads it out as you would speak it in Swedish. E.g, 100 -> etthundra, 765 -> sjuhundrasextiofem, and so on. This is a good exercise which forces you to use basic flow control, functions and recursion. This is the result:
module Main (main) where
import System (getArgs)
speak :: Int -> String
speak 0 = ""
speak 1 = "ett"
speak 2 = "två"
speak 3 = "tre"
speak 4 = "fyra"
speak 5 = "fem"
speak 6 = "sex"
speak 7 = "sju"
speak 8 = "åtta"
speak 9 = "nio"
speak 10 = "tio"
speak 11 = "elva"
speak 12 = "tolv"
speak 13 = "tretton"
speak 14 = "fjorton"
speak 15 = "femton"
speak 16 = "sexton"
speak 17 = "sjutton"
speak 18 = "arton"
speak 19 = "nitton"
speak 20 = "tjugo"
speak 30 = "trettio"
speak 40 = "fyrtio"
speak 50 = "femtio"
speak 60 = "sextio"
speak 70 = "sjuttio"
speak 80 = "åttio"
speak 90 = "nittio"
speak n = let
hundreds = floor(fromIntegral(n) / 100)
tens = computeTens n hundreds
single = fromIntegral(n) `mod` 10
in
if hundreds > 0 then
speak(hundreds) ++ "hundra" ++ speak(tens * 10 + single)
else
speak(tens * 10) ++ speak(single)
computeTens n hundreds =
let withoutSingle = floor(fromIntegral(n) / 10)
in withoutSingle - hundreds * 10
parse args = read (head args)::Int
main = do
args <- getArgs
print (speak (parse args))
I think the program ended up being quite nice in Haskell. Writing it was not entirely easy, though. Haskell is statically typed and uses type inference, and this system can be quite hostile to newcomers. I think it is a system which pays off more as you learn the language since you don't have to clutter your code with type declarations. As a beginner, though, it can be quite puzzling. For instance, I never quite understood why I have to use fromIntegral in the let block or why the fromIntegral calls seems to produce different types depending on if it goes into a division or modulo expression.I think certain parts of the language design causes the compiler to be confusing. For instance, computeTens initially took one argument. I added another argument later while developing. Unfortunately, I forgot to change the caller to provide the second argument. In most languages you would have gotten something like 'function computeTens expects 2 arguments, got 1'. In Haskell, it will say 'expected Int, inferred t -> t1'. The reason for this is that Haskell uses function currying in all invocations, so calling a function with too few arguments will just produce another function. This shows that you need certain skill to decrypt what the compiler tells you.
Also, Haskell has some annoying idiosyncrasies. For instance, there is an Int and an Integer type, and they are NOT the same thing.
Other than those gripes, I thought it was quite fun to use and seems to be really good system. Now that I have the basics nailed down I will start looking at Monads and other interesting ideas Haskell brings to the table. I'll see if I can rediscover some of the techniques with lazy evaluation and higher order functions I learned in school as well. More on that later!
gud, det där blir så mycket snyggare i Erlang alltså.. :)
SvaraRaderaJaså? Code or it didn't happen ;)
SvaraRaderaAa... baa.. jag slängde ihop nått som funkar.. men det går nog att göra snyggare om man orkar tänka lite till :)
SvaraRadera-module(speak).
-compile(export_all).
speak_to_string(N)
when is_integer(N), N >= 1, N =< 19 ->
ens(N);
speak_to_string(N)
when is_integer(N), N >= 20, N < 100 ->
tens(N);
speak_to_string(N)
when N >= 100 ->
hundreds(N).
ens(N) ->
lists:nth(N, ["ett", "två", "tre", "fyra", "fem", "sex", "sju", "åtta", "nio", "tio",
"elva", "tolv", "tretton", "fjorton", "femton", "sexton", "sjutton", "arton", "nitton"]).
tens(N) ->
Tens = ["tjugo", "trettio", "fyrtio", "femtio", "sextio", "sjuttio", "åttio", "nittio"],
case N rem 10 of
0 -> lists:nth(trunc(N/10)-1, Tens);
Rest -> lists:nth(trunc(N/10)-1,Tens) ++ ens(Rest)
end.
hundreds(N) ->
case N rem 100 of
0 -> ens(trunc(N/100)) ++ "hundra";
Rest when Rest >= 20 -> ens(trunc(N/100)) ++ "hundra" ++ tens(Rest);
Rest -> ens(trunc(N/100)) ++ "hundra" ++ ens(Rest)
end.
speak(N) ->
speak_to_string(N).