我目前正在研究欧拉项目40问题的实现,并试图找出如何在Haskell中从列表中取出多个项目而无需重新开始列表。
目前,我有一个Integral a => [a]
类型的列表champernowne
,它将返回Champernowne常数数字的无限列表,然后我从这个序列中取出第一项,第十项等,并将它们相乘得到答案。实际代码是:
ans = (product . map (champernowne !!)) [0, 9, 99, 999, 9999, 99999]
这个实现的问题是(我假设)Haskell每次想要获得一个新项时都会从序列的开始遍历列表。我怎样才能让haskell只遍历从元素1到1 000 000的序列然后把中间的项取出来呢?我已经尝试了scanl,希望惰性求值能帮助我,但它没有:
ans = (product . head . scanl (flip drop) champernowne) [10, 90, 900, 9000, 90000]
只是为了澄清,代码的第一部分是工作的,但我正在努力改进我的实现,使其更有效。
解决这个问题的有效方法是不构造列表而直接计算数字。
但是如果你想要的索引是按升序给出的,你可以通过计算相对偏移量和drop
ping适当数量的项目来达到下一个想要的索引
-- supposes the list of indices in ascending order
indices :: [a] -> [Int] -> [a]
indices xs is = go xs offsets
where
offsets = zipWith (-) is (0:is)
go ys (k:ks) = case drop k ys of
z:zs -> z : go (z:zs) ks
_ -> []
go _ [] = []
你只是在第二个表达式中有一个小错误:在那里使用map head
而不是head
。
map (list !!) xs -- [0, 9, 99, 999, 9999, 99999] ==
map head . tail . scanl (flip drop) list $ zipWith (-) xs (0:xs)
-- [9, 90, 900, 9000, 90000]