簡體   English   中英

反轉列表的前k個元素

[英]Reverse first k elements of a list

我想有效地反轉列表的前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]

這是相當不錯的,但(++)困擾我。 或者我應該考慮使用其他數據結構? 我想用短名單做這么多次。

讓我們考慮reverse的通常結構:

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實際檢查過,所以一切正常。 查看生成的核心(使用ghc -O2 -ddump-simpl -dsuppress-all -dno-suppress-type-signatures )表明這些對被編譯成未提升的對,並且Int是未裝箱的,所以一切都應該是相當的高效。


分析表明,此實現的速度大約是差異列表的1.3倍,並且分配大約65%的內存。

好吧,通常我只是寫splitAt 3 >>> first reverse >>> 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個元素。 實際性能將取決於編譯器能夠融合的內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM