Number to word form

Rashakil Fol 0 Tallied Votes 331 Views Share

Converts any integer number (such as 123456000999) into a string ("one billion two hundred ninety-three million one hundred two thousand one hundred").

module Wordify (wordifyWords, wordify) where

digiteenNames       :: [String]
digiteenNames       = ["", "one", "two", "three", "four", "five"
                      ,"six", "seven", "eight", "nine", "ten"
                      ,"eleven", "twelve", "thirteen", "fourteen"
                      ,"fifteen","sixteen", "seventeen"
                      ,"eighteen", "nineteen"

-- Returns the string form of a particular digit.
digiteen            :: Integer -> String
digiteen n          = digiteenNames !! fromIntegral n

tenNames            :: [String]
tenNames            = ["","","twenty", "thirty", "forty", "fifty"
                      ,"sixty", "seventy", "eighty", "ninety"

-- Returns the string form for a particular multiple of ten.
tenName             :: Integer -> String
tenName n           = tenNames !! fromIntegral n

-- Converts a two digit number to its string form.
twodigit            :: Integer -> String
twodigit n | n < 20        = digiteen n
           | units == 0    = tenName tens
           | otherwise     = tenName tens ++ "-" ++ digiteen units
    where (tens, units) = divMod n 10

-- Converts a three digit number to an array of words.  For example:
--   threedigit 54  = ["fifty-four"]
--   threedigit 923 = ["nine", "hundred", "twenty-three"]
--   threedigit 400 = ["four", "hundred"]

threedigit          :: Integer -> [String]
threedigit n
    | hundi == 0    = [twodigit n]
    | twodi == 0    = [digiteen hundi, "hundred"]
    | otherwise     = [digiteen hundi, "hundred", twodigit twodi]
    where (hundi, twodi) = divMod n 100

-- periodNames = [[], ["thousand"], ["million"], ...
-- This list uses fake names after decillion.
periodNames         :: [[String]]
periodNames         = [] : map (:[]) ps
    where ps = ["thousand", "million", "billion", "trillion"
               ,"quadrillion", "quintillion", "sextillion"
               ,"octillion", "septillion", "novemtillion"
               ,"decillion"] ++ fakes 11
          fakes n = (wordify n ++ "-tillion") : fakes (n + 1)

-- Converts a large (or small) positive number to a list of words.
manydigit           :: Integer -> [String]
manydigit m         = aux periodNames m []
    where aux _      0 tail = tail
          aux (p:ps) n tail
              = let (n', front) = divMod n 1000
                in aux ps n' (case front of
                                0 -> tail
                                _ -> threedigit front ++ p ++ tail)

-- Converts any integer to word form, making a list of words.
wordifyWords        :: Integer -> [String]
wordifyWords n
    | n < 0         = "negative" : manydigit (negate n)
    | n == 0        = ["zero"] -- a special case!
    | otherwise     = manydigit n

-- Converts an integer to string form.  For example,
-- wordify 123456000999 = "one hundred twenty-three billion \
--                        \four hundred fifty-six million \
--                        \nine hundred ninety-nine"
wordify             :: Integer -> String
wordify             = unwords . wordifyWords
Rashakil Fol 978 Super Senior Demiposter Team Colleague

To evaluate the function, run ghci, load the file Wordify.hs with :l Wordify , and type wordify 123456000999 at the prompt (or some other argument).

Lardmeister 461 Posting Virtuoso

Why does Haskell give such huge executable files? I compiled a simple "Hello world!" program using 'ghc --make hello.hs' and came up with an 'hello.exe' file of over 700 kb!

Rashakil Fol 978 Super Senior Demiposter Team Colleague

I don't know. Because there's a bit of work and code that has to be executed for the garbage collector and other systems, I guess.

Member Avatar for gwern

Lardmeister: a couple reasons. First, every GHC executable comes with the RTS, runtime system. This handles garbage collection, threading (or lack there of), profiling, and so on. There's actually quite a few options for it. This RTS eats up a couple hundred KB.
Second, the executables are statically linked - there is no libh along the lines of libc. Dynamic/shared libraries are coming; I'm not sure when, but I heard the GHC devs got it working on some non-Linux platform already, so my guess would be 6.10.
And third, I believe GHC by default leaves all the debugging symbols and whatnot in. Running strip might reclaim some space.

Finally, I'd like to note that binary size isn't so terrible since a lot of the code size is a fixed cost as I mentioned - it looks worst for a hello world. My customized XMonad, when stripped, is about 2.4 megabytes, while your hello world example would lead one to suspect a size considerably worse... Also, come on, this is an old and uninteresting topic. Does it bother you when you run a Python or Java or Common Lisp app that the interpreter is like 20-60 megs?

v6ak 0 Newbie Poster

It is great. However, there is probably a mistake: There slould be:

| otherwise     = [digiteen hundi, "hundred and", twodigit twodi]

instead of:

| otherwise     = [digiteen hundi, "hundred", twodigit twodi]

(line 41).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.