Split a number into its digits with Haskell

Viewed 58061

Given an arbitrary number, how can I process each digit of the number individually?

Edit I've added a basic example of the kind of thing Foo might do.

For example, in C# I might do something like this:

static void Main(string[] args)
{
    int number = 1234567890;
    string numberAsString = number.ToString();

    foreach(char x in numberAsString)
    {
        string y = x.ToString();
        int z = int.Parse(y);
        Foo(z);
    }
}

void Foo(int n)
{
    Console.WriteLine(n*n);
}
17 Answers

I was lazy to write my custom function so I googled it and tbh I was surprised that none of the answers on this website provided a really good solution – high performance and type safe. So here it is, maybe somebody would like to use it. Basically:

  1. It is type safe - it returns a type checked non-empty list of Word8 digits (all the above solutions return a list of numbers, but it cannot happen that we get [] right?)
  2. This one is performance optimized with tail call optimization, fast concatenation and no need to do any reversing of the final values.
  3. It uses special assignment syntax which in connection to -XStrict allows Haskell to fully do strictness analysis and optimize the inner loop.

Enjoy:

{-# LANGUAGE Strict #-}

digits :: Integral a => a -> NonEmpty Word8
digits = go [] where
    go s x = loop (head :| s) tail where
        head = fromIntegral (x `mod` 10)
        tail = x `div` 10
    loop s@(r :| rs) = \case
        0 -> s
        x -> go (r : rs) x

I've been following next steps(based on this comment):

  1. Convert the integer to a string.
  2. Iterate over the string character-by-character.
  3. Convert each character back to an integer, while appending it to the end of a list.

toDigits :: Integer -> [Integer]
toDigits a = [(read([m])::Integer) | m<-show(a)]

main = print(toDigits(1234))

I would like to improve upon the answer of Dave Clarke in this page. It boils down to using div and mod on a number and adding their results to a list, only this time it won't appear reversed, nor resort to ++ (which is slower concatenation).

toDigits :: Integer -> [Integer]

toDigits n
  | n <= 0    = []
  | otherwise = numToDigits (n `mod` 10) (n `div` 10) []
    where
      numToDigits a 0 l = (a:l)
      numToDigits a b l = numToDigits (b `mod` 10) (b `div` 10) (a:l)

This program was a solution to a problem in the CIS 194 course at UPenn that is available right here. You divide the number to find its result as an integer and the remainder as another. You pass them to a function whose third argument is an empty list. The remainder will be added to the list in case the result of division is 0. The function will be called again in case it's another number. The remainders will add in order until the end.

Note: this is for numbers, which means that zeros to the left won't count, and it will allow you to have their digits for further manipulation.

Related