简体   繁体   English

在haskell中将整数列表转换为一个Int(如concat)

[英]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].我有一个像这样的整数列表:[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.我想将其更改为 Integer 123。我的第一个想法是 concat 但这不起作用,因为它的类型错误,我尝试了各种方法,但通常我最终会返回相同的列表。 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.此外,我找到了一种打印正确内容 (putStr) 的方法,但我希望类型为 Integer 而 putStr 不这样做。

You can use foldl to combine all the elements of a list:您可以使用foldl来组合列表的所有元素:

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. addDigit函数由foldl调用以从最左边的数字开始一个接一个地添加数字。

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

Edit:编辑:
foldl walks through the list from left to right, adding the elements to accumulate some value. foldl从左到右遍历列表,添加元素以累积一些值。

The second argument of foldl , 0 in this case, is the starting value of the process. foldl的第二个参数,在本例中为0 ,是进程的起始值。 In the first step, that starting value is combined with 1 , the first element of the list, by calling addDigit 0 1 .在第一步骤中,该初始值与组合1 ,该列表的第一元件,通过调用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.这导致 10*0+1 = 1。在下一步中,这个 1 与列表的第二个元素组合,通过addDigit 1 2 ,给出 10*1+2 = 12。然后这与列表的第三个元素组合列表,通过addDigit 12 3 ,导致 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:您可以concat数字的字符串表示,然后read它们read回,如下所示:

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))第 1 步 - 将列表中的每个 int 转换为字符串(map show (x:xs))
Step 2 - combine each of those strings together (concat (step 1))第 2 步 - 将每个字符串组合在一起(concat (step 1))
Step 3 - read the string as the type of int read (step 2) :: Int第 3 步 - 将字符串读取为 int read (step 2) :: Int的类型read (step 2) :: Int

Use read and also intToDigit :使用readintToDigit

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.另一个想法是:最后一位数为 1,倒数第二位为 10,前一位数为 100,依此类推。 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.因此,要将数字列表转换为数字,您需要将其反转(以便从后面开始),将数字与相应的 10 的幂相乘,然后将结果相加。

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!要反转列表,请使用reverse ,要获得十的幂,您可以使用iterate (*10) 1 (在 GHCi 或 Hugs 中尝试!),将两个列表的相应数字相乘使用zipWith (*)并将所有内容加在一起,使用sum - 了解一些库函数真的很有帮助! 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.但是,此解决方案比foldl解决方案慢,这是由于调用了reverse并且您正在构建 10 的幂,只是为了再次直接使用它们。 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 .从好的方面来说,这种构建数字的方式更接近人们通常的思考方式(至少我是这样!),而foldl -solutions 本质上使用了Horner 的规则

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.原因是putStr只能打印字符串。 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.您可能还想尝试 Prelude 的print功能。 This one can print anything that is "showable" (any instance of class Show ), not only Strings.这个可以打印任何“可显示”的东西(类Show任何实例),而不仅仅是字符串。 But be aware that print n corresponds (roughly) to putStrLn (show n) , not putStr (show n) .但请注意, print n对应于(大致) putStrLn (show n) ,而不是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.我们可以定义名为join的函数,该函数给定一个整数列表,它可以返回另一个整数。 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.我不是 Haskell 的专家,但这是我能想到的解决此问题的最简单方法,不涉及使用任何其他外部函数。

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.在我的解决方案中,首先我定义了一个名为reverseDigits的函数,它反转原始列表。 For example [1,2,3] to [3,2,1]例如 [1,2,3] 到 [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.之后,我使用了一个concatReversed函数,它接受一个数字列表和数字 d,这是列表位置第一个数字的十次幂的结果。 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.如果列表为空,则返回 0,如果不是,则返回列表中的第一个数字乘以 d,加上对concatReversed的调用传递列表的其余部分和 d 乘以 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.很长一段时间后,我发现我的解决方案非常混乱,因为它需要反转列表,以便能够从右到左将每个数字乘以列表中数字索引的 10 次方。 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.现在知道元组,我看到更好的方法是有一个函数来接收累积的转换部分和列表的其余部分,因此在每次调用中将累积部分乘以 10,然后添加当前数字。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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