Performance 索引处的破坏列表

Performance 索引处的破坏列表,performance,haskell,Performance,Haskell,今天我有一个性能问题 我正在制作一个(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是

今天我有一个性能问题

我正在制作一个(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
的实例

解决方案 我使用模块中的
序列调整了代码。结果如下:

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
导入符合条件的数据。顺序为S
断开:Int->Seq a->(a,Seq a)
突破Nxs=(指数为0,指数为1)
哪里
(ys,zs)=S.splitAt(n-1)xs

但是,我仍然接受进一步的改进建议

是的,这是低效的。通过使用
splitAt
(在递归位期间取消编号),您可以做得更好,通过使用具有有效拆分的数据结构(例如a)可以做得更好,最好通过调整上下文来避免需要此操作。如果您发布更多的上下文,可能会给出更有针对性的建议。

前奏曲函数通常非常有效。您可以使用
splitAt
重写函数,如下所示:

breakOn :: Int -> [a] -> (a,[a])
breakOn n xs = (z,ys++zs)
 where
  (ys,z:zs) = splitAt (n-1) xs

为什么不使用标准功能?Breakn n l=(取n l,取n l)@nist没有正确的类型。mhwombat下面的答案是这样的。因为我需要保留第n个元素,并在单个列表中包含提取元素前后的所有元素。好的,谢谢!我不知道!完成!谢谢,这个版本效率更高一些,但仍然很慢。我在这里使用大列表。我将尝试使用Daniel Wagner建议的
Data.Sequence
模块。这个版本的问题是正确的附加(我认为)。@DanielDíaz不,这很好。此版本的唯一问题是获取列表的第
n
-th元素是一个
O(n)
操作。如果速度不够快,则需要不同的数据结构
Data.Sequence
是一个很好的第一个候选者。@Danielfisher是的,我正在尝试使整个代码适应Data.Sequence。现在我正在尝试为
Seq
类型实现
mapM
。好的,这是一个巨大的性能改进!我调整了我的程序,使其在任何地方都使用
Sequence
s。现在它的性能好多了。有趣的是,现在生成随机数要比拆分列表花费更多的时间。万岁!我剩下的问题是为什么列表是默认方法?@DanielDíaz,因为它们非常简单(使用和实现都很简单)。它们的速度足够快,可用于多种用途。只是,索引不是其中之一。我真的必须反对,这里有太多叫Daniel的人让我无法跟踪[@C.A.McCann我不同意。丹尼尔和丹尼尔是众所周知的哈斯凯尔顶级回答者,而丹尼尔,因为OP是蓝色突出显示的,是一个开朗、开放、快速学习的OP。我认为没有理由感到困惑。顺便说一句,丹尼尔,如果你继续快速学习,你会像丹尼尔和丹尼尔一样有洞察力。