Reputation: 545
Let’s consider 2 lists: ["a","b","c"]
and ["a","b","c","d","e","f"]
I want to check if the first list is the beginning of the other list I thought I could use:
["a","b","c"] == head (splitAt (length ["a","b","c"]) ["a","b","c","d","e","f"])
Unfortunately this doesn't work. Is there another way to get the first the 3 first elements out of a list in a new list?
Upvotes: 6
Views: 9441
Reputation: 7266
I want to check if the first list is the beginning of the other list.
You can use isPrefixOf
from the Data.List module.
Upvotes: 3
Reputation: 54058
Rather than using take
, you could use zipWith
to avoid traversing the lists twice. When you call length
, you first have to traverse the shorter list, then you take that many values from the longer list, then you traverse the lists, comparing element by element. What would make more sense is to traverse both lists as the same time, stopping your comparison when the shorter one is expired. zipWith
provides exactly this functionality:
-- Definitions for `and` and `zipWith` in `Prelude`
--
-- and :: [Bool] -> Bool
-- and [] = True
-- and (x:xs) = x && and xs
--
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
-- zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys
-- zipWith _ _ _ = []
sameStartingElements :: Eq a => [a] -> [a] -> Bool
sameStartingElements xs ys = and $ zipWith (==) xs ys
Thanks to laziness, this definition will only traverse both lists once, and it stops as soon as one of them runs out of elements. This will be somewhat more efficient, and it avoids having to know the length of either list.
Upvotes: 8