简体   繁体   中英

Apply a function to every element in a list to every element in another list - Haskell

My ultimate goal is to find if a list y contains all the elements of list x (I'm checking if x is a subset of y sort of thing)

subset x y =
and [out | z <- x
         , out <- filter (==z) y ]

This doesn't work, and I know it's because z is a list still. I'm trying to make sense of this.

I think I may have to use the elem function, but I'm not sure how to split x into chars that I can compare separately through y.

I'm ashamed to say that I've been working on this simple problem for an hour and a half.

Checking whether all elements of xs are elements of ys is very straightforward. Loop through xs , and for each element, check if it is in ys :

subset xs ys = all (\x -> elem x ys) xs

You could also use the list difference function ( \\\\ ). If you have list y and list x, and you want to check that all elements of x are in y, then x \\\\ y will return a new list with the elements of x that are not in y. If all the elements of x are in y, the returned list will be empty.

For example, if your list y is [1,2,3,4,5] and your list x is [2,4], you can do:

Prelude> [2,4] \\ [1,2,3,4,5]
[]

If list y is [1,2,3,4,5] and list x is [2,4,6], then:

Prelude> [2,4,6] \\ [1,2,3,4,5]
[6]

Easy way to reason about subsets is to use sets as the data type.

import qualified Data.Set as S

subset :: Ord a => [a] -> [a] -> Bool
subset xs ys = S.isSubsetOf (S.fromList xs) (S.fromList ys)

Then it's as simple as:

*Main> subset [1..5] [1..10]
True
*Main> subset [0..5] [1..10]
False

Let's break this down into two subproblems:

  1. Find if a value is a member of a list;
  2. Use the solution to #1 to test whether every value in a list is in the second one.

For the first subproblem there is a library function already:

elem :: (Eq a, Foldable t) => a -> t a -> Bool

Lists are a Foldable type, so you can use this function with lists for t and it would have the following type:

elem :: (Eq a) => a -> [a] -> Bool

EXERCISE: Write your own version of elem , specialized to work with lists (don't worry about the Foldable stuff now).

So now, to tackle #2, one first step would be this:

-- For each element of `xs`, test whether it's an element of `ys`.
-- Return a list of the results.
notYetSubset :: Eq a => [a] -> [a] -> [Bool]
notYetSubset xs ys = map (\x -> elem x ys) xs

After that, we need to go from the list of individual boolean results to just one boolean. There's a standard library function that does that as well:

-- Return true if and only if every element of the argument collection is 
-- is true.
and :: Foldable t => t Bool -> Bool

EXERCISE: write your own version of and , specialized to lists:

myAnd :: [Bool] -> Bool
myAnd [] = _fillMeIn
myAnd (x:xs) = _fillMeIn

With these tools, now we can write subset :

subset :: Eq a => [a] -> [a] -> [Bool]
subset xs ys = and (map (\x -> elem x ys) xs)

Although a more experienced Haskeller would probably write it like this:

subset :: Eq a => [a] -> [a] -> [Bool]
subset xs ys = every (`elem` ys) xs

{- This:

       (`elem` ys)

   ...is a syntactic shortcut for this:

       \x -> x elem ys
-}

...where every is another standard library function that is just a shortcut for the combination of map and and :

-- Apply a boolean test to every element of the list, and
-- return `True` if and only if the test succeeds for all elements.
every :: (a -> Bool) -> [a] -> Bool
every p = and . map p

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM