tokola
tokola

Reputation: 257

Whats wrong with this scala tuple?

Trying to create a method that determines if a set is a subset of another set, both given as parameters. When I tried to test it the console printed out

scala.MatchError : (List(1, 2, 3, 4, 5, 6, 7),List(1, 2, 3, 4)) (of class scala.Tuple2),

the two lists given are what I was using as parameters to test it. Also, scala was making me type out return in front of true and false, any ideas what led to this either?

def subset(a: List[Int], b: List[Int]): Boolean ={
(a,b) match {
    case (_,Nil)=> return true
  }
  b match {
    case h::t if (a.contains(h)) => subset(a,t)
    case h::t => return false
  }}

Upvotes: 0

Views: 470

Answers (5)

Puneeth Reddy V
Puneeth Reddy V

Reputation: 1578

MatchError occurs whenever an object doesn't match any pattern of a pattern matching expression.

An alternative way is by using dropWhile or takeWhile

def subsets(a:List[Int], b:List[Int]):Boolean = {
return b.dropWhile { ele => a.contains(ele)}.size==0
}

OR

def subsets(a:List[Int], b:List[Int]):Boolean = {
return b.takeWhile { ele => a.contains(ele)}.size==b.size
}

Upvotes: 0

Michael Zajac
Michael Zajac

Reputation: 55569

The other answers don't really answer exactly why your code is incorrect. It appears that you're handling the case when list b is empty and non-empty and that everything should be okay, but in fact you're actually not. Let's look at your code again, with some formatting fixes.

def subset(a: List[Int], b: List[Int]): Boolean = {
  (a, b) match {
     case (_, Nil) => return true
  } // we can never make it past here, because either we return true,
    // or a MatchError is raised.

  b match {
    case h :: t if (a.contains(h)) => subset(a,t)
    case h :: t => return false
  }
}

The real problem here is that you have two completely disconnected match statements. So when b is non-empty, the first match will fail, because it only handles the case when b is Nil.

As pointed out in the other solutions, the proper way to do this is to merge the two match statements together into one.

def subset(a: List[Int], b: List[Int]): Boolean = {
    (a, b) match {
        case (_, Nil) => true
        case (xs, head :: tail) if(xs contains head) => subset(xs, tail)
        case _ => false
    }
}

Notice how the return statements are no longer needed. In scala you should avoid using return as much as possible, as it's likely that your way of thinking around return actually lead you into this trap. Methods that return early are likely to lead to bugs, and are difficult to read.

A cleaner way to implement this could use diff. b can be considered a subset of a if the set of elements of b minus the elements of a is empty.

def subset(a: List[Int], b: List[Int]): Boolean = (b.distinct diff a.distinct).nonEmpty

distinct is only required if it's possible for a and b to contain duplicates, because we're trying a List like a Set when it's actually not.

Better yet, if we convert the Lists to Sets, then we can use subsetOf.

def subset(a: List[Int], b: List[Int]): Boolean = b.toSet.subsetOf(a.toSet)

Upvotes: 5

Chong Tang
Chong Tang

Reputation: 2146

An alternative way could be to call methods in standard library.

For each element in 'b', check if 'a' contains that element.

Here is the simple code:

def subset(a: List[Int], b: List[Int]): Boolean = {
  (b.forall(a.contains(_)))
}

Upvotes: 1

Scala match expression should match to at least one case expression. Otherwise the MatchError is raised.

You should have used the following cases:

(a, b) match {
  case (_, Nil) => true
  case (aa, h :: t) if aa contains h => subset(aa, t)
  case _ => false
}

Upvotes: 1

Rich Henry
Rich Henry

Reputation: 1849

MatchError - This class implements errors which are thrown whenever an object doesn't match any pattern of a pattern matching expression.

Obviously the second list having elements in it will cause this error, as no pattern will match. You should just add another branch to the first match like so:

    def subset(a: List[Int], b: List[Int]): Boolean = {
      (a, b) match {
        case (_, List()) => return true
        case _ => b match {
          case h :: t if (a.contains(h)) => subset(a, t)
          case h :: t => return false
        }
      }
    }

Upvotes: 0

Related Questions