Convert list of Integers into one Int (like concat) in haskell
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.
Related videos on Youtube
Paul
Updated on April 15, 2022Comments
-
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áš over 14 yearsOr way cooler point free - fromDigits = foldl ((+).(*10)) 0 :)
-
Paul over 14 yearsAwesome! 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 over 14 yearsThis turns
[12,34]
into1234
-- unlike sth's, which turns it into154
, or Chris's, which errors out. OP's desired behavior is not specified enough. -
Paul over 14 yearsSo 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 over 14 yearssth'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 over 14 yearsYou'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'saddDigit 0 1
= 1,addDigit 1 2
= 12,addDigit 12 3
= 123 -
ephemient over 14 yearsIf 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
, ifaddDigit
were a left-associative infix operator. Then again, having the 0 in front doesn't change a thing... -
Paul over 14 yearsAh I get it now I was just forgetting that it's accumulative. Thanks!
-
Chuck over 14 yearsTo 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 over 14 yearsIs that list valid syntax in GHC? All I've got here is Hugs, and it sure doesn't work there.
-
yatima2975 over 14 yearsNo, 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 over 14 yearsThis 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 over 14 yearsUse
foldl'
instead. Found inData.List
. -
Thomas Eding over 14 years-1: Non-constructor functions are lowercase. Also not very intuitive IMO.
-
Fede over 14 yearsI 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 almost 5 yearsit 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 almost 5 yearsHi @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 almost 5 yearsHi @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.