Reputation: 8079
I've been trying to understand the isInfixOf
function from the Data.List
module, to no avail. Here's the code:
isInfixOf :: (Eq a) => [a] -> [a] -> Bool
isInfixOf needle haystack = any (isPrefixOf needle) (tails haystack)
-- Example:
--
-- >isInfixOf "Haskell" "I really like Haskell." -> True
-- >isInfixOf "Ial" "I really like Haskell." -> False
The any
returns True if at least one item in the list fulfills the condition. It's type is (a -> Bool) -> [a] -> Bool
The isPrefixOf
and the tails
functions codes are these:
isPrefixOf :: (Eq a) => [a] -> [a] -> Bool
isPrefixOf [] _ = True
isPrefixOf _ [] = False
isPrefixOf (x:xs) (y:ys) = x == y && isPrefixOf xs ys
tails :: [a] -> [[a]]
tails xs = xs : case xs of
[] -> []
_ : xs' -> tails xs'
-- Example:
-- tails "abc" == ["abc", "bc", "c",""]
I've read about curried functions and partial applied functions but I couldn't understand it yet, neither could I understand how this particular function works. How can it work if any
takes lists, not a list of lists, as a parameter?
Upvotes: 1
Views: 4132
Reputation: 532
We could read isInfixOf needle haystack = any (isPrefixOf needle) (tails haystack)
as: return True
if needle
isPrefixOf
any
subdivision of haystack
taking away the first elements (i.e., taking the tails
) of it.
Isn't it clear yet? Let's try another approach, using your example.
isInfixOf "Haskell" "I really like Haskell."
Giving that String
is the same as [Char]
, we have the following signature for your example:
isInfixOf :: (Eq [Char]) => [Char] -> [Char] -> Bool
So far so good, huh? Let's see any
? No. Let's see isPrefixOf
, because it is actually called first and fill the type blanks of any
.
isPrefixOf :: (Eq Char) -> [Char] -> [Char] -> Bool
So what isPrefixOf
doing? It is comparing, Char
by Char
, if the first String
(or array of Char
s) is in the begining of the second String
. Now, let's get back to any
: any :: (a -> Bool) -> [a] -> Bool
. But the function being passed to any
, in the example, is isPrefixOf needle
, i.e., isPrefixOf "Haskell"
. Or, in other words, the function begin passed to any
, of the type (a -> Bool)
, is actually (String -> Bool)
or, if you prefer, ([Char] -> Bool)
. Got it? It is about the partial application functions: isPrefixOf
is, originally (in our example),
isPrefixOf :: (Eq Char) -> [Char] -> [Char] -> Bool
However, I am passing, as a function, isPrefixOf "Haskell"
, so it is fixed as isPrefixOf "Haskell" :: "Haskell" -> [Char] -> Bool
, a notation that doesn't exist as you can imagine. Let's make it "real"? Just take away what seems strange:
isPrefixOf "Haskell" :: [Char] -> Bool
or
isPrefixOf "Haskell" :: String -> Bool
Back, again, to any
. As you can imagine, any
, now, is
any :: (String -> Bool) -> [String] -> Bool
or
any :: ([Char] -> Bool) -> [[Char]] -> Bool
Well, I think that now the idea of tails
is totally clear for you.
tails "I really like Haskell." :: String -> [String]
or
tails "I really like Haskell." :: [Char] -> [[Char]]
At the end, the execution is:
isInfixOf "Haskell" "I really like Haskell." =
any (isPrefixOf "Haskell") (tails "I really like Haskell.") =
any (isPrefixOf "Haskell") [
"I really like Haskell.",
" really like Haskell.",
"really like Haskell.",
"eallylike Haskell.",
"ally like Haskell.",
"lly like Haskell.",
"ly like Haskell.",
"y like Haskell.",
" like Haskell.",
"like Haskell.",
"ike Haskell.",
"ke Haskell.",
"e Haskell.",
"Haskell.",
"Haskell.",
"askell.",
"skell.",
"kell.",
"ell.",
"ll.",
"l.",
".",
""]
Well, it is easy to very that "Haskell"
is prefix of "Haskell."
.
I hope I have explained it clearly. :)
Upvotes: 6