我目前拥有Haskell函数,该函数将整数转换为从原始整数中取得的数字列表。我的问题是:有没有办法在不使用mod
和div
的情况下执行此操作?例如,如果我想用字符串做同样的事情,我可以使用其他函数创建一个函数,如head和tail等。
我在这个问题上挣扎了一段时间才终于来到SO并在另一篇文章中找到答案。是什么让我问这个问题的事实是我从未想过自己使用mod
和div
!
toDigits :: Integer -> [Integer]
toDigits n
| n < 1 = []
| otherwise = toDigits (n `div` 10) ++ [n `mod` 10]
答案 0 :(得分:8)
你提到你可以对带有列表操作的字符串做同样的事情。实际上,这将是另一种方式。您可以将整数转换为字符串,然后将每个字符转换为整数:
import Data.Char (digitToInt)
toDigits :: Int -> [Int]
toDigits = map digitToInt . show
我在这里使用的是Int
而不是Integer
,但如果您真的想要更多麻烦,可以使用Integer
:
toDigits :: Integer -> [Integer]
toDigits = map (fromIntegral . digitToInt) . show
答案 1 :(得分:2)
@icktoofay's answer使用show
,这是将某些值转换为String
的通用方法(换句话说,获取其字符串表示形式)。值应该是类型类Show
的实例的类型。例如,Int
是Show
的一个实例(在:i Int
中输入ghci
并寻找字符串instance Show Int -- Defined in `GHC.Show'
)。但是函数不是Show
的实例,因此let f n = n in f
会抛出错误,因为如何将函数转换为字符串? (另见:If functions as instances of the Show typeclass)。无论如何,使用show
函数是惯用的,所以你可以坚持下去。
然而,有一种方法可以使用对数,幂和整数除法从数字中提取数字。请记住,您可以通过查找余数来从左侧删除数字,并通过整数除法从右侧删除数字。在这两种情况下,右操作数都是10的幂。例如:
*Main> 123 `mod` 10
3
*Main> 123 `div` 100
1
但是你怎么知道,你应该使用哪个10的幂除以?通过找到对数基数10:#digits N = log 10 N + 1,例如, log 10 12345 = 4.遗憾的是,您不能使用logBase
,因为它使用浮点运算,即inaccurate。例如:
*Main> logBase 10 1000
2.9999999999999996
您可以使用自定义函数iLogBase
进行整数复制 - 将链接中的代码复制到源代码中。这种方法可以找到数字的第一个数字,我使用以下代码:
firstDigit :: (Integral a) => a -> a
firstDigit n = n `div` (10^log)
where log = fst $ iLogBase 10 n
创建一个更通用的函数来查找数字的任意数字并将数字转换为数字列表留给你作为练习:)。
此外,您问题中的代码效率低下。列表连接(++)
操作具有O(n)
的复杂性,也就是说,每次要将元素追加到列表末尾时,它必须将左侧列表逐个添加到右侧列表中,直到你有一个结果列表。查看(++)
的{{3}},基本上[1,2,3] ++ [4]
变为1 : 2 : 3 : [4]
,这非常低效,因为仅添加列表需要3次(:)
操作。当你多次将数字追加到末尾时,它必须每次重复相同的过程,因此你的函数的整体复杂性为O(n^2)
。
另一方面,(:)
是即时的,即具有O(1)
的复杂性。无论你的列表有多长,将元素添加到开头都很便宜。因此,我建议不要在最后添加元素,而是将其添加到开头,最后只需将列表反转一次(有关信息,Lisp人称之为source):
reverse $ (n `mod` 10) : toDigits (n `div` 10)