简体   繁体   中英

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

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.

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.

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

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

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

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.

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 .

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) .

join :: Integral a => [a] -> a
join [x] = x
join (x:xs) = (x * (10 ^ long)) + join(xs)
    where long = length(x:xs)

We can define the function called join , that given a list of Integral numbers it can return another Integral number. We are using recursion to separate the head of the given list with the rest of the list and we use pattern matching to define an edge condition so that the recursion can end.

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 returns the first digit on the list times d, plus the call to concatReversed passing the rest of the list and d times 10.

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


Edit

After a long time, I see my solution is very messy, as it requires reversing the list in order to be able to multiply each digit by 10 power the index of the digit in the list, from right to left. Now knowing tuples, I see that a much better approach is to have a function that receives both the accumulated converted part, and the remainder of the list, so in each invocation in multiplies the accumulated part by 10, and then adds the current digit.

concatDigits :: [Int] -> Int
concatDigits xs = aggregate (xs, 0)
  where aggregate :: ([Int], Int) -> Int
        aggregate ([], acc) = acc
        aggregate (x:xs, acc) = aggregate (xs, (acc * 10 + x))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM