views:

677

answers:

6

Pretty much what the title says. I have a list of Integers like so: [1,2,3]. I want to change this in to the Integer 123. My first thought was concat but that doesn't work because it's of the wrong type, I've tried various things but usually I just end up returning the same list. Any help greatly appreciated.

Also I have found a way to print the right thing (putStr) except I want the type to be Integer and putStr doesn't do that.

+14  A: 

You can use foldl to combine all the elements of a list:

fromDigits = foldl addDigit 0
   where addDigit num d = 10*num + d

The addDigit function is called by foldl to add the digits, one after another, starting from the leftmost one.

*Main> fromDigits [1,2,3]
123

Edit:
foldl walks through the list from left to right, adding the elements to accumulate some value.

The second argument of foldl, 0 in this case, is the starting value of the process. In the first step, that starting value is combined with 1, the first element of the list, by calling addDigit 0 1. This results in 10*0+1 = 1. In the next step this 1 is combined with the second element of the list, by addDigit 1 2, giving 10*1+2 = 12. Then this is combined with the third element of the list, by addDigit 12 3, resulting in 10*12+3 = 123.

So pointlessly multiplying by zero is just the first step, in the following steps the multiplication is actually needed to add the new digits "to the end" of the number getting accumulated.

sth
Or way cooler point free - fromDigits = foldl ((+).(*10)) 0 :)
Matajon
Awesome! Works perfectly. This was what I thinking I'd have to do but you've done it much nicer than I could so thanks!
Paul
So it works, but I'm not exactly sure how the addDigit part works. I know foldl takes a function and a list does that on all the elements of the list.. but addDigit seems to multiply 0 (num) by 10 and then add the element.. which would just be adding the element.. so what am I missing?
Paul
You're missing that `foldl` accumulates as it goes--0 is used the first time, and the result is used the second time, etc. So it's `addDigit 0 1` = 1, `addDigit 1 2` = 12, `addDigit 12 3` = 123
camccann
If someone felt strongly enough about the pointless 0, they could remove it and use `foldl1` instead. And perhaps that's easier to explain? `1 addDigit 2 addDigit 3`, if `addDigit` were a left-associative infix operator. Then again, having the 0 in front doesn't change a thing...
ephemient
Ah I get it now I was just forgetting that it's accumulative. Thanks!
Paul
To make this work for multiple-digit numbers, you just need to change the 10 in `addDigit` to `(10 ^ (floor $ log (fromIntegral d) / log 10 + 1))`. It looks a little ugly, but all it's doing is adjusting 10 by the magnitude of the number.
Chuck
This doesn't do the right thing for multidigit numbers. Chuck's solution works, but the basic idea of what is going on is better expressed in Ashutosh Mehra's solution -- directly smashing together the textual representation.
wnoise
Use `foldl'` instead. Found in `Data.List`.
trinithis
+6  A: 

You could concat the string representations of the numbers, and then read them back, like so:

joiner :: [Integer] -> Integer
joiner = read . concatMap show
Ashutosh Mehra
This turns `[12,34]` into `1234` -- unlike sth's, which turns it into `154`, or Chris's, which errors out. OP's desired behavior is not specified enough.
ephemient
sth's works because I'm only ever using 1 digit numbers. This is also the way I'd like it to work.. but could someone explain precisely how the addDigit works in sth's?
Paul
+1  A: 

Use read and also intToDigit:

joinInt :: [Int] -> Int
joinInt l = read $ map intToDigit l

Has the advantage (or disadvantage) of puking on multi-digit numbers.

Chris Lutz
A: 

As for how to print the number, instead of

putStr n

just try

putStr (show n)

The reasoning is that putStr can only print strings. So you need to convert the number to a string before passing it in.

You may also want to try the print function from Prelude. This one can print anything that is "showable" (any instance of class Show), not only Strings. But be aware that print n corresponds (roughly) to putStrLn (show n), not putStr (show n).

Thiago Arrais
By the way, this should really be a separate question...
Thiago Arrais
+1  A: 

Another idea would be to say: the last digit counts for 1, the next-to last counts for 10, the digit before that counts for 100, etcetera. So to convert a list of digits to a number, you need to reverse it (in order to start at the back), multiply the digits together with the corresponding powers of ten, and add the result together.

To reverse a list, use reverse, to get the powers of ten you can use iterate (*10) 1 (try it in GHCi or Hugs!), to multiply corresponding digits of two lists use zipWith (*) and to add everything together, use sum - it really helps to know a few library functions! Putting the bits together, you get

fromDigits xs = sum (zipWith (*) (reverse xs) (iterate (*10) 1))

Example of evaluation:

fromDigits [1,2,3,4]  
    ==> sum (zipWith (*) (reverse [1,2,3,4]) [1,10,100,1000, ....]
    ==> sum (zipWith (*) [4,3,2,1] [1,10,100,1000, ....])
    ==> sum [4 * 1, 3 * 10, 2 * 100, 1 * 1000]
    ==> 4 + 30 + 200 + 1000
    ==> 1234

However, this solution is slower than the ones with foldl, due to the call to reverse and since you're building up those powers of ten only to use them directly again. On the plus side, this way of building numbers is closer to the way people usually think (at least I do!), while the foldl-solutions in essence use Horner's rule.

yatima2975
Is that list valid syntax in GHC? All I've got here is Hugs, and it sure doesn't work there.
Chuck
No, that list is just for explaining what (part of) the result of `iterate (*10) 1` looks like. It doesn't work in GHC either.
yatima2975
A: 

Hi, I'm no expert in Haskell, but this is the easiest way I can think of for a solution to this problem that doesn't involve using any other external functions.

ConcatDigits :: [Int] -> Int
ConcatDigits [] = 0
ConcatDigits xs = ConcatReversed (ReverseDigits xs) 1

ReverseDigits :: [Int] -> [Int]
ReverseDigits [] = []
ReverseDigits (x:xs) = (ReverseDigits xs) : x

ConcatReversed :: [Int] -> Int -> Int
ConcatReversed [] d = 0
ConcatReversed (x:xs) d = (x*d) + ConcatReversed xs (d*10)

As you can see, I've assumed you're trying to concat a list of digits. If by any chance this is not your case, I'm pretty sure this won't work. :(

In my solution, first of all I've defined a function called ReverseDigits, which reverses the original list. For example [1,2,3] to [3,2,1]

After that, I use a ConcatReversed function which takes a list of digits and number d, which is the result of ten power the first digit on the list position. If the list is empty it returns 0, and if not, it return the first digit on the list times d, plus the call to ConcatReversed passing the rest of the list and d times ten.

Hope the code speak for itself, because I think my poor English explanation wasn't very helpful.

Fede
-1: Non-constructor functions are lowercase. Also not very intuitive IMO.
trinithis
I believe this is a perfectly valid answer. I was clear about I'm no expert in Haskell, so don't kill me if I don't know the coding conventions.
Fede