Преобразование неотрицательного Целого числа
в его список цифр обычно выполняется следующим образом:
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
Теперь тест. Примечание: я принудительно выполняю оценку, используя фильтр
.
λ>:set +s
λ>length $ filter (>5) $ concat $ map (digits) [1..1000000]
2400000
(1.58 secs, 771212628 bytes)
Это ссылка. Теперь для цифр2
:
λ>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)
цифр3
на 4,89 раз медленнее. Просто для развлечения, Я пробовал использовать только revDigits3 и избегать обратного
.
λ>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 времени медленнее.
У меня создалось впечатление, что только использование арифметики и cons превзойдет все, что связано с преобразованием строк. Видимо, я чего-то не понимаю.
Так в чем же фокус? Почему цифр
так быстро?
Я использую GHC 6.12.3.