Convert list of Integers into one Int (like concat) in haskell

18,466

Solution 1

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.

Solution 2

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

joiner :: [Integer] -> Integer
joiner = read . concatMap show

Solution 3

This worked pretty well for me.

read (concat (map show (x:xs))) :: Int

How function reads:
Step 1 - convert each int in the list to a string (map show (x:xs))
Step 2 - combine each of those strings together (concat (step 1))
Step 3 - read the string as the type of int read (step 2) :: Int

Solution 4

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.

Solution 5

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.

Share:
18,466

Related videos on Youtube

Paul
Author by

Paul

Updated on April 15, 2022

Comments

  • Paul
    Paul about 2 years

    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.

  • Martin Jonáš
    Martin Jonáš over 14 years
    Or way cooler point free - fromDigits = foldl ((+).(*10)) 0 :)
  • Paul
    Paul over 14 years
    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!
  • ephemient
    ephemient over 14 years
    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.
  • Paul
    Paul over 14 years
    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
    Paul over 14 years
    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?
  • C. A. McCann
    C. A. McCann over 14 years
    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
  • ephemient
    ephemient over 14 years
    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...
  • Paul
    Paul over 14 years
    Ah I get it now I was just forgetting that it's accumulative. Thanks!
  • Chuck
    Chuck over 14 years
    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
    Chuck over 14 years
    Is that list valid syntax in GHC? All I've got here is Hugs, and it sure doesn't work there.
  • yatima2975
    yatima2975 over 14 years
    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.
  • wnoise
    wnoise over 14 years
    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.
  • Thomas Eding
    Thomas Eding over 14 years
    Use foldl' instead. Found in Data.List.
  • Thomas Eding
    Thomas Eding over 14 years
    -1: Non-constructor functions are lowercase. Also not very intuitive IMO.
  • Fede
    Fede over 14 years
    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.
  • Will Ness
    Will Ness almost 5 years
    it is not just a coding convention. trying to define a function with a capitalized name, e.g. F 1 = 1 actually results in an error: "Not in scope: data constructor `F' ".
  • Fede
    Fede almost 5 years
    Hi @ThomasEding. I lowered the function names as an attempt to make peace. Let me highlight again I'm no expert in Haskell, not by a very long far. I'm sorry you didn't find my answer intuitive enough, even though I tried to provide enough detail in my explanation. I as a learner, find more useful my own answer, than relying on foldl, concat, map, zip or any other external function. I saw a question about something I was toying around a long time ago and treated it as a puzzle to solve. I don't even remember what I used in order to run and test the code.
  • Fede
    Fede almost 5 years
    Hi @WillNess. I lowered the function names as an attempt to make peace. Let me highlight again I'm no expert in Haskell, not by a very long far. I saw a question about something I was toying around a long time ago and treated it as a puzzle to solve. I don't even remember what I used in order to run and test the code.