在haskell中将整数列表转换为一个Int(如concat)
Posted
技术标签:
【中文标题】在haskell中将整数列表转换为一个Int(如concat)【英文标题】:Convert list of Integers into one Int (like concat) in haskell 【发布时间】:2010-12-27 11:18:06 【问题描述】:和标题说的差不多。我有一个整数列表,如下所示:[1,2,3]。我想将其更改为整数 123。我的第一个想法是 concat 但这不起作用,因为它的类型错误,我尝试了各种方法,但通常我最终只返回相同的列表。非常感谢任何帮助。
此外,我已经找到了一种打印正确内容 (putStr) 的方法,但我希望类型为 Integer 而 putStr 不这样做。
【问题讨论】:
【参考方案1】:您可以使用foldl
组合列表的所有元素:
fromDigits = foldl addDigit 0
where addDigit num d = 10*num + d
addDigit
函数由foldl
调用,从最左边开始,一个接一个地添加数字。
*Main> fromDigits [1,2,3]
123
编辑:foldl
从左到右遍历列表,添加元素以累积一些值。
foldl
的第二个参数,在本例中为0
,是进程的起始值。在第一步中,该起始值通过调用addDigit 0 1
与列表的第一个元素1
组合。这导致 10*0+1 = 1。在下一步中,这个 1 与列表的第二个元素组合,addDigit 1 2
,得到 10*1+2 = 12。然后它与列表的第三个元素组合列表,addDigit 12 3
,得到 10*12+3 = 123。
因此,毫无意义地乘以零只是第一步,在接下来的步骤中,实际上需要乘以将新数字添加到累积数字的“末尾”。
【讨论】:
或者更酷点免费 - fromDigits = foldl ((+).(*10)) 0 :) 你错过了foldl
不断累积——第一次使用 0,第二次使用结果,等等。所以它是 addDigit 0 1
= 1, @987654336 @ = 12, addDigit 12 3
= 123
如果有人对无意义的 0 感到足够强烈,他们可以删除它并改用 foldl1
。也许这更容易解释? 1 addDigit 2 addDigit 3
,如果 addDigit
是左结合中缀运算符。话又说回来,前面有 0 并不会改变任何事情......
要使这项工作适用于多位数字,您只需将addDigit
中的10 更改为(10 ^ (floor $ log (fromIntegral d) / log 10 + 1))
。它看起来有点难看,但它所做的只是将 10 调整为数字的大小。
这对多位数字不正确。 Chuck 的解决方案有效,但在 Ashutosh Mehra 的解决方案中更好地表达了正在发生的基本思想 - 直接将文本表示拼凑在一起。【参考方案2】:
你可以concat
数字的字符串表示,然后read
他们回来,像这样:
joiner :: [Integer] -> Integer
joiner = read . concatMap show
【讨论】:
这会将[12,34]
转换为1234
- 不像某事将其转换为154
或Chris 的会出错。 OP 的期望行为没有指定足够。
sth 的作品,因为我只使用 1 位数字。这也是我希望它工作的方式.. 但是有人可以准确解释 addDigit 在某事中的工作原理吗?【参考方案3】:
这对我来说效果很好。
read (concat (map show (x:xs))) :: Int
函数的读取方式:
第 1 步 - 将列表中的每个 int 转换为字符串
(map show (x:xs))
第 2 步 - 将这些字符串中的每一个组合在一起
(concat (step 1))
第 3 步 - 将字符串读取为 int 类型
read (step 2) :: Int
【讨论】:
【参考方案4】:使用read
和intToDigit
:
joinInt :: [Int] -> Int
joinInt l = read $ map intToDigit l
具有呕吐多位数的优势(或劣势)。
【讨论】:
【参考方案5】:另一个想法是说:最后一位数为 1,倒数第二个数为 10,之前的一位数为 100,等等。因此,要将数字列表转换为数字,您需要将其反转(以便从后面开始),将数字与相应的十的幂相乘,然后将结果相加。
要反转列表,请使用reverse
,要获得十的幂,您可以使用iterate (*10) 1
(在 GHCi 或 Hugs 中尝试!),将两个列表的对应数字相乘使用 zipWith (*)
,然后将所有内容相加一起使用sum
- 了解一些库函数真的很有帮助!把这些位放在一起,你得到
fromDigits xs = sum (zipWith (*) (reverse xs) (iterate (*10) 1))
评估示例:
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
然而,这个解决方案比foldl
的解决方案要慢,因为调用了reverse
,而且你正在建立十的幂,只是为了再次直接使用它们。从好的方面来说,这种构建数字的方式更接近人们通常的想法(至少我是这样!),而foldl
-解决方案本质上使用Horner's rule。
【讨论】:
该列表在 GHC 中是否有效?我这里只有拥抱,它肯定在那里不起作用。 不,该列表仅用于解释iterate (*10) 1
的结果(部分)是什么样的。它在 GHC 中也不起作用。【参考方案6】:
至于如何打印数字,而不是
putStr n
试试吧
putStr (show n)
原因是putStr
只能打印字符串。所以你需要在传入之前将数字转换为字符串。
您可能还想尝试 Prelude 中的 print
函数。这个可以打印任何“可显示”的东西(Show
类的任何实例),而不仅仅是字符串。但请注意,print n
(大致)对应于putStrLn (show n)
,而不是putStr (show n)
。
【讨论】:
【参考方案7】:join :: Integral a => [a] -> a
join [x] = x
join (x:xs) = (x * (10 ^ long)) + join(xs)
where long = length(x:xs)
我们可以定义一个名为join
的函数,给定一个整数列表,它可以返回另一个整数。我们使用递归将给定列表的头部与列表的其余部分分开,并使用模式匹配来定义边缘条件,以便递归可以结束。
【讨论】:
【参考方案8】:我不是 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)
如您所见,我假设您正在尝试连接数字列表。如果这不是你的情况,我很确定这不会起作用。 :(
在我的解决方案中,首先我定义了一个名为reverseDigits
的函数,它反转了原始列表。例如 [1,2,3] 到 [3,2,1]
之后,我使用了一个concatReversed
函数,它接受一个数字列表和数字 d,它是列表位置第一个数字的十次方的结果。如果列表为空,则返回 0,如果不是,则返回列表上的第一个数字乘以 d,加上对 concatReversed
的调用传递列表的其余部分和 d 乘以 10。
希望代码能说明问题,因为我认为我糟糕的英文解释没有多大帮助。
编辑
很长一段时间后,我发现我的解决方案非常混乱,因为它需要反转列表,以便能够将每个数字乘以列表中数字索引的 10 次方,从右到左。现在知道元组,我发现一个更好的方法是有一个函数来接收累积的转换部分和列表的其余部分,因此在每次调用中将累积的部分乘以 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))
【讨论】:
-1:非构造函数是小写的。 IMO 也不是很直观。 我相信这是一个完全有效的答案。我很清楚我不是 Haskell 专家,所以如果我不了解编码约定,请不要杀了我。 这不仅仅是一个编码约定。尝试使用大写名称定义函数,例如F 1 = 1
实际上会导致错误:“不在范围内:数据构造函数 `F'”.
嗨@ThomasEding。我降低了函数名称以求和解。让我再次强调一下,我不是 Haskell 的专家,不是很长时间。很抱歉,即使我试图在解释中提供足够的细节,您也没有发现我的答案足够直观。作为一名学习者,我发现自己的答案比依赖 foldl、concat、map、zip 或任何其他外部函数更有用。我看到一个关于我很久以前玩弄的东西的问题,并将其视为一个难题来解决。我什至不记得我用什么来运行和测试代码。
嗨@WillNess。我降低了函数名称以求和解。让我再次强调一下,我不是 Haskell 的专家,不是很长时间。我看到一个关于我很久以前玩弄的东西的问题,并将其视为一个难题来解决。我什至不记得我用什么来运行和测试代码。以上是关于在haskell中将整数列表转换为一个Int(如concat)的主要内容,如果未能解决你的问题,请参考以下文章
如何使用“ord”函数在 Haskell 中将 Char 转换为 Int?