Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
List 反转列表的前k个元素_List_Haskell - Fatal编程技术网

List 反转列表的前k个元素

List 反转列表的前k个元素,list,haskell,List,Haskell,我想有效地反转列表的前k个元素 这就是我的想法: reverseFirst :: Int -> [a] -> [a] -> [a] reverseFirst 0 xs rev = rev ++ xs reverseFirst k (x:xs) rev = reverseFirst (k-1) xs (x:rev) reversed = reverseFirst 3 [1..5] mempty -- Result: [3,2,1,4,5] 这相当不错,但是(++)让我

我想有效地反转列表的前k个元素

这就是我的想法:

reverseFirst :: Int -> [a] -> [a] -> [a]
reverseFirst 0 xs rev     = rev ++ xs
reverseFirst k (x:xs) rev = reverseFirst (k-1) xs (x:rev)

reversed = reverseFirst 3 [1..5] mempty -- Result: [3,2,1,4,5]

这相当不错,但是
(++)
让我很困扰。或者我应该考虑使用另一种数据结构吗?我想用简短的列表来做这件事无数次。

好吧,通常我只需要写
splitAt 3>>第一次反转>>>uncurry(++)
来实现这个目标

如果你对绩效感到焦虑,你可以考虑一个差异列表:

reverseFirstN :: Int -> [a] -> [a]
reverseFirstN = go id
 where go rev 0 xs = rev xs
       go rev k (x:xs) = go ((x:).rev) (k-1) xs

但坦率地说,我并不认为这会快得多:无论哪种方式,都需要遍历前n个元素。实际性能将在很大程度上取决于编译器能够融合的内容。

让我们考虑一下
反转的常见结构:

reverse = rev [] where
  rev acc [] = acc
  rev acc (x : xs) = rev (x : acc) xs
它从空列表开始,从参数列表的前面开始添加元素,直到完成。我们想做一些类似的事情,除了我们想把元素钉在列表中不反转的部分的前面。当我们还没有那未反转的部分时,我们怎么能做到这一点呢

我能想到的避免两次遍历列表前面的最简单方法是使用惰性:

reverseFirst :: Int -> [a] -> [a]
reverseFirst k xs = dis where
  (dis, dat) = rf dat k xs

  rf acc 0 ys = (acc, ys)
  rf acc n [] = (acc, [])
  rf acc n (y : ys) = rf (y : acc) (n - 1) ys
dat
表示列表中未列出的部分。我们在执行反转的同一个助手函数
rf
中计算它,但我们也在初始调用中将它传递给
rf
。它从来没有真正在
rf
中检查过,所以一切正常。查看生成的core(使用
ghc-O2-ddump siml-dsuppress all-dno suppress type signatures
)表明,这些对被编译成未折叠的对,而
Int
s被解除绑定,因此一切可能都非常有效



分析表明,该实现的速度大约是差异列表1的1.3倍,分配的内存大约是差异列表1的65%。

我觉得代码还行。我想真正的问题是:你为什么要这样做?我想解决这个问题。目前的解决方案既慢又难看。对。因此,这是一个基准测试,看看你能多快地交换东西,而不是产生一个实际有用的结果……如果你是为了一个基准测试,而不是其他使用数组而不是链表的语言,我会使用数组而不是链表。否则,这种比较是非常不公平的,IMHO。目前的解决方案确实使用数组。我不知道为什么你认为它是“缓慢而丑陋的”,但是…或者为什么你认为链接列表会胜过数组。如果我正确地读取了STG(大IF),那么你就为每个反向元素建立了一个闭包,至少在GHC 7.83.