[英]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.