将非负Integer
转换为其数字列表通常是这样做的:
import Data.Char
digits :: Integer -> [Int]
digits = (map digitToInt) . show
我试图找到一种更直接的方式来执行任务,而不涉及字符串转换,但我无法提出更快的内容。
到目前为止我一直在尝试的事情:
基线:
digits :: Int -> [Int]
digits = (map digitToInt) . show
从StackOverflow上的另一个问题得到这个:
digits2 :: Int -> [Int]
digits2 = map (`mod` 10) . reverse . takeWhile (> 0) . iterate (`div` 10)
尝试自己动手:
digits3 :: Int -> [Int]
digits3 = reverse . revDigits3
revDigits3 :: Int -> [Int]
revDigits3 n = case divMod n 10 of
(0, digit) -> [digit]
(rest, digit) -> digit:(revDigits3 rest)
这一启发灵感来自showInt
中的Numeric
:
digits4 n0 = go n0 [] where
go n cs
| n < 10 = n:cs
| otherwise = go q (r:cs)
where
(q,r) = n `quotRem` 10
现在是基准。注意:我正在使用filter
强制进行评估。
λ>:set +s
λ>length $ filter (>5) $ concat $ map (digits) [1..1000000]
2400000
(1.58 secs, 771212628 bytes)
这是参考。现在是digits2
:
λ>length $ filter (>5) $ concat $ map (digits2) [1..1000000]
2400000
(5.47 secs, 1256170448 bytes)
3.46 倍。
λ>length $ filter (>5) $ concat $ map (digits3) [1..1000000]
2400000
(7.74 secs, 1365486528 bytes)
digits3
4.89 时间较慢。只是为了好玩,我尝试只使用revDigits3并避开reverse
。
λ>length $ filter (>5) $ concat $ map (revDigits3) [1..1000000]
2400000
(8.28 secs, 1277538760 bytes)
奇怪的是,这甚至更慢, 5.24 倍慢。
最后一个:
λ>length $ filter (>5) $ concat $ map (digits4) [1..1000000]
2400000
(16.48 secs, 1779445968 bytes)
10.43 时间较慢。
我的印象是只使用算术和缺点会胜过涉及字符串转换的任何内容。显然,有些东西我无法掌握。
那么诀窍呢?为什么digits
如此之快?
我正在使用GHC 6.12.3。
答案 0 :(得分:30)
答案 1 :(得分:12)
回答“为什么rem而不是mod?”这个问题。在评论中。在处理正值rem x y === mod x y
时,唯一需要注意的是性能:
> import Test.QuickCheck
> quickCheck (\x y -> x > 0 && y > 0 ==> x `rem` y == x `mod` y)
那么性能如何呢?除非你有充分的理由不(并且懒惰不是一个好理由,也不是不知道Criterion)然后使用一个好的基准工具,我使用Criterion:
$ cat useRem.hs
import Criterion
import Criterion.Main
list :: [Integer]
list = [1..10000]
main = defaultMain
[ bench "mod" (nf (map (`mod` 7)) list)
, bench "rem" (nf (map (`rem` 7)) list)
]
运行此展示显示rem
明显优于mod
(使用-O2
编译):
$ ./useRem
...
benchmarking mod
...
mean: 590.4692 us, lb 589.2473 us, ub 592.1766 us, ci 0.950
benchmarking rem
...
mean: 394.1580 us, lb 393.2415 us, ub 395.4184 us, ci 0.950