3
\$\begingroup\$

This is a simple practice problem. The task is to write a function getLR that receives an Integral, n, and returns a list representation (LR) of n. For example: 73847 -> [7,3,8,4,7]. Negative numbers return the LR of the absolute value, for example: -837 -> [8,3,7]. In the following code I use three helper functions getAbs, getI and getList:

getLR :: (Integral a) => a -> [a]
getLR n = if n==0 then [0] else getAbs n

getAbs :: (Integral a) => a -> [a]
getAbs n = if n>=0 then getI n n 0 else getI (-n) (-n) 0

-- Computes the length of an Integer and stores it in i, for example 321 has length 3, 9999 has length 4. 
getI :: (Integral a) => a -> a -> a -> [a]
getI n m i = if m==0 then getList n [] (i-1) else getI n (m`div`10) (i+1)

-- Constructs the LR recursively.
getList :: (Integral a) => a -> [a] -> a -> [a]
getList n ns i = if n==0 then ns else getList (n-10^i*(n`div`(10^i))) (ns++[n`div`(10^i)]) (i-1)

The code works correctly. My specific concerns are readability and simplicity of the code. If you have any suggestions how to make the code more readable or more simple I'd be happy to receive your feedback. If you find any problems besides readability and simplicity: don't hesitate to point them out as well.

\$\endgroup\$
3
\$\begingroup\$

I'm not a Haskell programmer, but let me just point out a few things here.

First, expressions such as (n-10^i*(n`div`(10^i))) look quite cluttered. One thing you can do is just put a space between each operator and its operands, and with longer expressions, you can extract pieces and bind them to names to make it easier to understand what's going on. For example, you use 10^i multiple times. Perhaps you could use let fac = 10 ^ i in... to make it clearer what's happening.

Second, the names of these methods are a bit unclear. getAbs seems like it would return the absolute value instead of returning the list representation of a number's absolute value. Honestly, I would inline it as part of getLR. getLR's meaning isn't immediately clear, and I think listRepr or simply digits is a clearer name for it. Similarly, getI doesn't tell you what it does unless you read the comment above it. I would suggest using where to "hide" it, along with getList.

However, I think your implementation is a little complicated. All you need is to build up a list of digits, starting at the last digit, by using x `mod` 10 to get the next digit and x `div` 10 to get the rest of the number. Here's a solution that I find much simpler:

digits :: (Integral a) => a -> [a]
digits 0 = [0]
digits n = go (abs n) [] where
  go 0 digs = digs
  go x digs = go (x `div` 10) (x `mod` 10 : digs)
\$\endgroup\$
2
\$\begingroup\$

Interestingly enough, this can be solved with no math at all. The things to know are:

  1. show turns an Int into a String
  2. A String is internally actually [Char], so you can map to and from strings as you would lists.
  3. read converts a String to the datatype it represents (but does not work with Chars)

Putting those together, the steps to turn an integer into a list of integers is:

  1. show it
  2. Turn each character in the string into a string itself
  3. map a function with the signature String -> Int
digits :: Int -> [Int]
digits = map (read :: String -> Int) . map (:[]) . show

Running in a repl:

> let digits = map (read :: String -> Int) . map (:[]) . show
> digits 73487
=> [7,3,4,8,7]

Update to make it work for both Ints and Integers

digits :: (Integral a, Show a, Read a) => a -> [a]
digits = map (read . (:[])) . show

Running in a repl:

> digits :: (Integral a, Show a, Read a) => a -> [a]
  digits = map (read . (:[])) . show

> digits (12345::Int)
=> [1,2,3,4,5]
> digits (12345::Integer)
=> [1,2,3,4,5]
\$\endgroup\$
6
  • \$\begingroup\$ nice solution. however it seems to only work when digits receives Int. when passing the type Integer it doesn't seem to work, i.e. can you make it work with digits :: Integer -> [Integer]? \$\endgroup\$ – Moritz Wolff Jun 29 at 11:00
  • \$\begingroup\$ Not at first pass, but I'll give it another go later! \$\endgroup\$ – Oso Jun 29 at 14:21
  • \$\begingroup\$ @MoritzWolff Okay, it took me a while but it ended up being way easier than I thought... Just needed a different type signature. \$\endgroup\$ – Oso Jun 30 at 2:04
  • 1
    \$\begingroup\$ This is nice for some cases, but less general than OP's solution. Not only does it not work for types that don't implement Show and Read, but it also makes assumptions about show and read that don't hold in general. For an example of the latter, look at Identity Int (where Identity is from Data.Functor.Identity) - getLR (Identity 123) is [Identity 1, Identity 2, Identity 3], but digits (Identity 123) fails to produce a value \$\endgroup\$ – Sara J Jun 30 at 7:14
  • \$\begingroup\$ This looks like a good, concise approach too, but you might want to tack on . abs at the end of the digits function so that it works with negative numbers \$\endgroup\$ – user Jul 1 at 22:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.