How can I make a function that finds the common elements of two strings?
I want to learn how to create functions using mainly head and tail. What I need to know is the protocol or presentation of elements, how to identify the elements, and how to define the function. For example, I am practicing how to write a simple function to find the common elements of two strings. Like this:
commonElt :: (Eq a)=> [a]->[a]-> bool
commonElt [] _ = true
commonElt _ [] = false
commonElt s1 s2 |if elm (head s1)s2 == true then show head s1 && commonElt(tail s1)s2
|otherwise = commonElt(tail s1) s2
where s1
and s2
are strings. But I cannot l开发者_C百科oad the modules because I get the following error message: "parse error. possible incorrect identation."
Edit: julia
: Inevitably, what looks to be the same answer, from hammar
appeared while writing, but maybe there is some helpful difference.
julia
, as larsmans
says, one problem is that you are not capitalizing the names of types and their constructors. The definition of Bool is:
data Bool = True | False
the words on both sides are capitalized; Bool
is the name of the type, True
and False
are the constructors we match on when dividing cases. By contrast, if the new terms we define are functions and other values, we use lower case. Suppose I want to define not :: Bool -> Bool
, well there are two cases:
not True = False
not False = True
with or :: Bool -> Bool -> Bool
there are four cases, but we can shorten things.
or False False = False
or _ _ = True
So here, not
and or
are both introduced with in lower case. Now to get to your problem, the first thing to note is that the type signature and the first two cases,
commonElt :: (Eq a) => [a] -> [a] -> Bool
commonElt [] _ = True
commonElt _ [] = False
don't really correspond to your specification, which is to find the common elements. That would have the signature
commonElts :: (Eq a) => [a] -> [a] -> [a]
If we repair the first two lines to match that ambition we get
commonElts [] _ = []
commonElts _ [] = []
then, following the idea of your third case we can spare ourselves the use of head
and tail
if we note that in the case in question neither term will be constructed as []
but rather with :
, so the case is, say:
commonElts (s:ss) (t:ts)
Your idea was to check whether s
was in the second list, and then include it in a list that is built up by using commonElts
on the remaining bit, ss
. There are several ways of going about this, I will use if
, following your example. Note that Haskell's if...then..
is always if ... then --- else ---
; the first blank is the boolean test, the other two blanks can be of any type as long as it is the same, thus we usually line them up:
commonElts (s:ss) (t:ts) =
if elem s (t:ts) == True then s : commonElts ss (t:ts)
else commonElts ss (t:ts)
Note. though, that elem s (t:ts)
is already a Bool, True or False, so we don't need to add == True
. Furthermore, we don't actually use the division of the second list into t
and ts
, so we can just call it ts
or s2
as you did. Thus the complete definition is:
commonElts [] _ = []
commonElts _ [] = []
commonElts (s:s1) s2 =
if elem s s2 then s : commonElts s1 s2
else commonElts s1 s2
If an element appears more than once in the first list, it will appear more than once in the 'common element' list; we could resolve this with a further distinction of cases.
Edit: note that a very similar function can be defined with a 'list comprehension':
common xs ys = [ x | x <- xs , y <- ys, x == y]
Indentation matters in Haskell. You should align the clauses for commonElt
:
commonElt [] _ = true
commonElt _ [] = false
commonElt s1 s2 ...
Then realize that the boolean values are called True
and False
; Haskell is case sensitive.
You have a lot of errors here, and it's not clear exactly what your function should do, but here's an attempt to make some sense of it:
First, the lines definining commonElt
should not be indented. Also, Bool
, True
and False
are written with a leading upper case letter as they are constructors (Bool
is a type constructor, True
and False
are data constructors).
Secondly, you don't use if
in a guard like that. Just write the conditional on its own between the |
and the =
. Also, == True
is uneccessary, and I think you meant to use elem
, not elm
.
The rest, while syntactically valid, does not type check or make much sense. show
returns a String
which cannot be used with (&&)
because it only works on booleans (its type is Bool -> Bool -> Bool
).
commonElt :: (Eq a) => [a] -> [a] -> Bool
commonElt [] _ = True
commonElt _ [] = False
commonElt s1 s2 | elem (head s1) s2 = show (head s1) && commonElt (tail s1) s2
| otherwise = commonElt (tail s1) s2
At this point it's not clear to me what you're trying to do, but as you said in your problem statement that you were trying to find the common elements of two lists, I think you want something like this:
commonElt :: (Eq a) => [a] -> [a] -> [a]
commonElt [] _ = []
commonElt s1 s2 | elem (head s1) s2 = head s1 : commonElt (tail s1) s2
| otherwise = commonElt (tail s1) s2
Instead of returning a Bool
, we gather up a list of the elements in s1
that are also in s2
using the (:)
operator which builds a list out of a head element and a tail list.
Of couse, this could be written more succinctly using higher order functions:
commonElt s1 s2 = filter (`elem` s2) s1
This function already exists in the Data.List
module and is called intersect
.
精彩评论