我今天有一个表现问题。
我正在制作一个(Haskell)程序,在进行性能分析时,我发现大部分时间花在了下面的功能中。它的目的是获取列表的第n个元素,并返回除了元素本身之外没有它的列表。我目前的(慢)定义如下:
breakOn :: Int -> [a] -> (a,[a])
breakOn 1 (x:xs) = (x,xs)
breakOn n (x:xs) = (y,x:ys)
where
(y,ys) = breakOn (n-1) xs
已知Int
参数位于1..n
范围内,其中n
是(永不为空)列表(x:xs)
的长度,因此函数永远不会出现错误。
然而,我在这里表现不佳。我的第一个猜测是我应该更改另一个结构的列表。但是,在开始选择不同的结构和测试代码(这将花费我很多时间)之前,我想在这里询问第三人的意见。另外,我很确定我没有以最好的方式做到这一点。欢迎任何指示!
请注意,a
类型可能不是Eq
的实例。
我在Data.Sequence模块中调整了我的代码使用Sequence
s。结果如下:
import qualified Data.Sequence as S
breakOn :: Int -> Seq a -> (a,Seq a)
breakOn n xs = (S.index zs 0, ys <> (S.drop 1 zs))
where
(ys,zs) = S.splitAt (n-1) xs
但是,我仍然接受进一步的改进建议!
答案 0 :(得分:9)
是的,效率低下。使用splitAt
(在递归位期间将数字解除容器)可以做得更好,通过使用具有有效分割的数据结构,例如, a fingertree,最好通过按摩上下文来避免需要此操作。如果您发布更多上下文,则可以提供更有针对性的建议。
答案 1 :(得分:4)
Prelude功能通常非常有效。您可以使用splitAt
重写您的函数,如下所示:
breakOn :: Int -> [a] -> (a,[a])
breakOn n xs = (z,ys++zs)
where
(ys,z:zs) = splitAt (n-1) xs