Commit 49b05d69 authored by David Feuer's avatar David Feuer Committed by Herbert Valerio Riedel

Improve performance of isSuffixOf (#9676)

The new implementation avoids reversing the "haystack" list, which can be
very expensive.

Reviewed By: ekmett

Differential Revision: https://phabricator.haskell.org/D330
parent 0013613d
......@@ -292,11 +292,34 @@ isPrefixOf [] _ = True
isPrefixOf _ [] = False
isPrefixOf (x:xs) (y:ys)= x == y && isPrefixOf xs ys
-- | The 'isSuffixOf' function takes two lists and returns 'True'
-- iff the first list is a suffix of the second.
-- Both lists must be finite.
-- | The 'isSuffixOf' function takes two lists and returns 'True' iff
-- the first list is a suffix of the second. The second list must be
-- finite.
isSuffixOf :: (Eq a) => [a] -> [a] -> Bool
isSuffixOf x y = reverse x `isPrefixOf` reverse y
ns `isSuffixOf` hs = maybe False id $ do
delta <- dropLengthMaybe ns hs
return $ ns == dropLength delta hs
-- Since dropLengthMaybe ns hs succeeded, we know that (if hs is finite)
-- length ns + length delta = length hs
-- so dropping the length of delta from hs will yield a suffix exactly
-- the length of ns.
-- A version of drop that drops the length of the first argument from the
-- second argument. If xs is longer than ys, xs will not be traversed in its
-- entirety. dropLength is also generally faster than (drop . length)
-- Both this and dropLengthMaybe could be written as folds over their first
-- arguments, but this reduces clarity with no benefit to isSuffixOf.
dropLength :: [a] -> [b] -> [b]
dropLength [] y = y
dropLength _ [] = []
dropLength (_:x') (_:y') = dropLength x' y'
-- A version of dropLength that returns Nothing if the second list runs out of
-- elements before the first.
dropLengthMaybe :: [a] -> [b] -> Maybe [b]
dropLengthMaybe [] y = Just y
dropLengthMaybe _ [] = Nothing
dropLengthMaybe (_:x') (_:y') = dropLengthMaybe x' y'
-- | The 'isInfixOf' function takes two lists and returns 'True'
-- iff the first list is contained, wholly and intact,
......
......@@ -86,6 +86,7 @@ test('exceptionsrun002', normal, compile_and_run, [''])
test('list001' , when(fast(), skip), compile_and_run, [''])
test('list002', when(fast(), skip), compile_and_run, [''])
test('list003', when(fast(), skip), compile_and_run, [''])
test('isSuffixOf', normal, compile_and_run, [''])
test('memo001',
[extra_run_opts('+RTS -A10k -RTS'),
......
module Main (main) where
import Data.List
needles = ["","1","2","12","123","1234"]
haystacks = ["","a","ab","abc","1","2","3","a1","1a",
"23","123","a123","ab123","abc123"]
main :: IO()
main = mapM_ print $ [needle `isSuffixOf` haystack
| needle <- needles, haystack <- haystacks]
True
True
True
True
True
True
True
True
True
True
True
True
True
True
False
False
False
False
True
False
False
True
False
False
False
False
False
False
False
False
False
False
False
True
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
False
True
True
True
True
False
False
False
False
False
False
False
False
False
False
False
False
False
False
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment