这是我的功能签名:
foo :: Integer -> [String]
此功能应如何运作:
foo 1234567
应该返回["567", "234", "001"]
foo 12345678
应该返回["678", "345", "012"]
oo 123456789
应该返回["789", "456", "123"]
我目前的解决方案:
zeichenreihenBlock :: Integer -> [String]
zeichenreihenBlock x = reverse (partition (show x))
partition :: String -> [String]
partition [] = []
partition x
| length x `mod` 3 == 2 = take 3 ("0" ++ x) : partition (drop 3 ("0" ++ x))
| length x `mod` 3 == 1 = take 3 ("00" ++ x) : partition (drop 3 ("00" ++ x))
| length x `mod` 3 == 0 = take 3 x : partition (drop 3 x)
将'zeichenreihenBlock'替换为'foo'。你会如何实现这个?有更有效的解决方案吗?
真的很感激一些新想法。
干杯
答案 0 :(得分:6)
使用数字来获取组更容易,而不是使用字符串。
digitGroups group 0 = []
digitGroups group n = r : digitGroups group q
where (q,r) = n `divMod` (10^group)
-- > digitGroups 3 1234567
-- [567,234,1]
然后使用填充显示成为一个单独的问题:
pad char len str = replicate (len - length str) char ++ str
-- > pad '0' 3 "12"
-- "012"
他们聚在一起寻找最终解决方案:
foo = map (pad '0' 3 . show) . digitGroups 3
-- > foo 1234567
-- ["567","234","001"]
答案 1 :(得分:3)
先填充,分割,反向:
foo :: Integer -> [String]
foo = reverse . split3 . pad0
split3 [] = []
split3 xs = take 3 xs : split3 (drop 3 xs)
pad0 x = let s = show x in replicate ((- length s) `mod` 3) '0' ++ s
测试:
ghci> foo 1234567
["567","234","001"]
ghci> foo 12345678
["678","345","012"]
ghci> foo 123456789
["789","456","123"]
答案 2 :(得分:2)
你可以经常检查长度方式(O(n)操作),然后你可以修复每个案例,所以需要付出一些代价:
import Data.List.Grouping(splitEvery)
foo :: Integer -> [String]
foo = map (reverse . fixIt) . splitEvery 3 . reverse . show
where
fixIt [a] = a:'0':'0':[]
fixIt [a,b] = a:b:'0':[]
fixIt lst = lst
另一种方法是检查列表ONCE的长度并在第一个函数中添加填充。这避免了单个列表遍历的成本的额外逆转(注意节省不多)。在go
中,我只假设列表的长度为mod 3
(因为它是)并始终为3。
import Data.List.Grouping
foo :: Integer -> [String]
foo x | r == 2 = go ('0':s)
| r == 1 = go ('0':'0':s)
| otherwise = go s
where
r = l `rem` 3
l = length s
s = show x
go = reverse . splitEvery 3
并不是说这里的表现有点重要(其他代码会占主导地位)但是我喜欢用Criterion打造好玩的东西:
benchmarking doubleRev -- my first one
mean: 14.98601 us, lb 14.97309 us, ub 15.00181 us, ci 0.950
benchmarking singleRev -- my second one
mean: 13.64535 us, lb 13.62470 us, ub 13.69482 us, ci 0.950
benchmarking simpleNumeric -- this is sepp2k
mean: 23.03267 us, lb 23.01467 us, ub 23.05799 us, ci 0.950
benchmarking jetxee -- jetxee beats all
mean: 10.55556 us, lb 10.54605 us, ub 10.56657 us, ci 0.950
benchmarking original
mean: 21.96451 us, lb 21.94825 us, ub 21.98329 us, ci 0.950
benchmarking luqui
mean: 17.21585 us, lb 17.19863 us, ub 17.25251 us, ci 0.950
-- benchmarked heinrich at a later time
-- His was ~ 20us
此外,这是一个很好的机会,可以指出你对最快速度的最佳猜测通常不会消失(至少,不适合我)。如果您想要优化配置文件和基准测试,请不要猜测。
答案 3 :(得分:2)
使用整数运算的简单解决方案:
padWithZeros n | n < 10 = "00" ++ show n
| n < 100 = '0' : show n
| otherwise = show n
foo 0 = []
foo n = padWithZeros (n `mod` 1000) : foo (n `div` 1000)
答案 4 :(得分:1)
嗯,我认为如果你使用更多的模式匹配而不是那些守卫会更好看。您可以匹配特定的列表长度,如下所示:
partition :: String -> [String]
partition [] = ...
partition [x] = ...
partition [x,y] = ...
partition (x:y:z:rest) = ...
如果在拆分之前颠倒字符串也可能会更好,检查长度不是很优雅。这样你一次可以取三个数字,按正确顺序放回,然后重复。担心长度只会在最后发生,并且可以通过模式匹配完成(如上所述)。
答案 5 :(得分:0)
其他解决方案非常好,但真正的解决方案是使用无限列表进行填充。 ; - )
foo n = take count . map reverse . group 3 . (++ repeat '0') . reverse $ digits
where
digits = show n
count = (length digits - 1) `div` 3 + 1
group k xs = take k xs : group k (drop k xs)