@icktoofay's answer uses show
, a generic way to convert some value to a String
(in other words, get its string representation). A value should be of a type that is an instance of a typeclass Show
. For example, Int
is an instance of Show
(enter :i Int
in ghci
and seek for a string instance Show Int -- Defined in `GHC.Show'
). But a function isn't an instance of Show
, so let f n = n in f
will throw an error, because how would you convert a function to a string? (See also: If functions as instances of the Show typeclass). Anyway, using show
function is idiomatic, so you can stick to it.
There is however a way to extract a digit from a number using logarithms, powers and integer divisions. Remember that you can remove digits from the left by finding a remainder, and remove digits from the right by integer division. In both cases, the right operand is some power of 10. For example:
*Main> 123 `mod` 10
3
*Main> 123 `div` 100
1
But how do you know, which power of 10 you should use to divide by? By finding a logarithm base 10: #digits of N = log10N + 1, e.g. log1012345 = 4. Unfortunately you can't use logBase
, because it uses floating point arithmetic, which is inaccurate. For example:
*Main> logBase 10 1000
2.9999999999999996
You can use custom function iLogBase
for integers—copy the code from the link into your source code. This way to find a first digit of a number I use the following code:
firstDigit :: (Integral a) => a -> a
firstDigit n = n `div` (10^log)
where log = fst $ iLogBase 10 n
Creating a more general function of finding an arbitrary digit of a number and converting a number into a list of digits is left to you as an exercise :).
Also, the code in your question is inefficient. List concatenation (++)
operation has the complexity of O(n)
, that is, every time you want to append an element to and end of list, it has to add the left list to the right list one by one, until you have a resulting list. Check out the source for (++)
, basically [1,2,3] ++ [4]
becomes 1 : 2 : 3 : [4]
, which is terribly inefficient, as it takes 3 cons (:)
operations just to add a list. And as you append numbers to the end multiple times, it has to repeat the same process each time, therefore overall complexity of your function is O(n^2)
.
On the other hand (:)
is instant, that is, has complexity of O(1)
. No matter how long is your list, prepending an element to the beginning is cheap. So instead of adding an element to the end, I would recommend, adding it to the beginning and an the end simply reversing the list once (for information, Lisp people call this push/nreverse idiom):
reverse $ (n `mod` 10) : toDigits (n `div` 10)