Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala pattern matching with disjunctions not working

Tags:

scala

I am learning Scala and don't understand why the following is not working.

I want to refactor a (tested) mergeAndCount function which is part of a counting inversions algorithm to utilize pattern matching. Here is the unrefactored method:

def mergeAndCount(b: Vector[Int], c: Vector[Int]): (Int, Vector[Int]) = {
  if (b.isEmpty && c.isEmpty)
    (0, Vector())
  else if (!b.isEmpty && (c.isEmpty || b.head < c.head)) {
    val (count, r) = mergeAndCount(b drop 1, c)
    (count, b.head +: r)
  } else {
    val (count, r) = mergeAndCount(b, c drop 1)
    (count + b.length, c.head +: r)
  }
}

Here is my refactored method mergeAndCount2. Which is working fine.

def mergeAndCount2(b: Vector[Int], c: Vector[Int]): (Int, Vector[Int]) = (b, c) match {
  case (Vector(), Vector()) =>
    (0, Vector())
  case (bh +: br, Vector()) =>
    val (count, r) = mergeAndCount2(br, c)
    (count, bh +: r)
  case (bh +: br, ch +: cr) if bh < ch =>
    val (count, r) = mergeAndCount2(br, c)
    (count, bh +: r)
  case (_, ch +: cr) =>
    val (count, r) = mergeAndCount2(b, cr)
    (count + b.length, ch +: r)
}

However as you can see the second and third case are duplicate code. I therefore wanted to combine them using the disjunction like this:

  case (bh +: br, Vector()) | (bh +: br, ch +: cr) if bh < ch =>       
    val (count, r) = mergeAndCount2(br, c)
    (count, bh +: r)

This gives me an error though (on the case line): illegal variable in pattern alternative.

What am I doing wrong?

Any help (also on style) is greatly appreciated.

Update: thanks to your suggestions here is my result:

@tailrec
def mergeAndCount3(b: Vector[Int], c: Vector[Int], acc : (Int, Vector[Int])): (Int, Vector[Int]) = (b, c) match {
  case (Vector(), Vector())  =>
    acc
  case (bh +: br, _) if c.isEmpty ||  bh < c.head =>
    mergeAndCount3(br, c, (acc._1, acc._2 :+ bh))
  case (_, ch +: cr) =>
    mergeAndCount3(b, cr, (acc._1 + b.length, acc._2 :+ ch))
}
like image 814
Lodewijk Bogaards Avatar asked Nov 25 '25 16:11

Lodewijk Bogaards


1 Answers

When pattern matching with pipe (|) you are not allowed to bind any variable other than wildcard (_).

This is easy to understand: in the body of your case, what would be the actual type of bh or br for example if your two alternatives match different types?

Edit - from the scala reference:

8.1.11 Pattern Alternatives Syntax: Pattern ::= Pattern1 { ‘|’ Pattern1 } A pattern alternative p 1 | . . . | p n consists of a number of alternative patterns p i . All alternative patterns are type checked with the expected type of the pattern. They may no bind variables other than wildcards. The alternative pattern matches a value v if at least one its alternatives matches v.

Edit after first comment - you can use the wildcard to match something like this for example:

try {
 ...
} catch {
  case (_: NullPointerException | _: IllegalArgumentException) => ...
}
like image 170
vptheron Avatar answered Nov 28 '25 10:11

vptheron