Scala: Pattern matching when one of two items meets some condition - scala

I'm often writing code that compares two objects and produces a value based on whether they are the same, or different, based on how they are different.
So I might write:
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case (Some(value), None)) => "b"
case (None, Some(value)) => "b"
case _ = > "c"
}
Those 2nd and 3rd cases are the same really, so I tried writing:
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case (Some(value), None)) || (None, Some(value)) => "b"
case _ = > "c"
}
But no luck.
I encounter this problem in a few places, and this is just a specific example, the more general pattern is I have two things, and I want to know if one and only one of them meet some predicate, so I'd like to write something like this:
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case OneAndOnlyOne(value, v: Option[Foo] => v.isDefined ) => "b"
case _ = > "c"
}
So the idea here is that OneAndOnlyOne can be configured with a predicated (isDefined in this case) and you can use it in multiple places.
The above doesn't work at all, since its backwards, the predicate needs to be passed into the extractor not returned.
How about something like this?
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case new OneAndOnlyOne(v: Option[Foo] => v.isDefined )(value) => "b"
case _ = > "c"
}
with:
class OneAndOnlyOne[T](predicate: T => Boolean) {
def unapply( pair: Pair[T,T] ): Option[T] = {
val (item1,item2) = pair
val v1 = predicate(item1)
val v2 = predicate(item2)
if ( v1 != v2 )
Some( if ( v1 ) item1 else item2 )
else
None
}
}
But, this doesn't compile.
Can anyone see a way to make this solution work? Or propose another solution? I'm probably making this more complicated than it is :)

I think you're asking two slightly different questions.
One question is how to use "or" in switch statements. || doesn't work; | does. And you can't use variables in that case (because in general they might match different types, which renders the type confusing). So:
def matcher[T](a: (T,T)) = {
a match {
case (Some(x),Some(y)) => "both"
case (Some(_),None) | (None,Some(_)) => "either"
case _ => "none"
}
}
Another question is how to avoid having to do this over and over, especially if you want to be able to get at the value in the tuple. I've implemented a version here for Option, but you could use an unwrapped tuple and a boolean.
One trick to achieve this is that to prewrap the values before you start matching on it, and then use your own matching constructs that do what you want. For instance,
class DiOption[+T] {
def trinary = this
}
case class Both[T](first: T, second:T) extends DiOption[T] { }
case class OneOf[T](it: T) extends DiOption[T] { }
case class Neither() extends DiOption[Nothing] { }
implicit def sometuple2dioption[T](t2: (Option[T],Option[T])): DiOption[T] = {
t2 match {
case (Some(x),Some(y)) => Both(x,y)
case (Some(x),None) => OneOf(x)
case (None,Some(y)) => OneOf(y)
case _ => Neither()
}
}
// Example usage
val a = (Some("This"),None)
a trinary match {
case Both(s,t) => "Both"
case OneOf(s) => "Just one"
case _ => "Nothing"
}

If you have to support arbitrary predicates you can derive from this (which is based on Daniel's idea):
List(v1, v2) filter (_ %2 == 0) match {
case List(value1, value2) => "a"
case List(value) => "b"
case _ => "c"
}
the definition of the function:
def filteredMatch[T,R](values : T*)(f : T => Boolean)(p: PartialFunction[List[T], R]) : R =
p(List((values filter f) :_* ))
Now you can use it like this:
filteredMatch(v1,v2)(_ %2 == 0){
case List(value1, value2) => "a"
case List(value) => "b"
case _ => "c"
}
I'm not so sure if it's a good idea (i.e. readable). But a neat exercise nonetheless.
It would be nice if you could match on tuples: case (value1, value2) => ... instead of lists.

How about this:
Welcome to Scala version 2.8.0.r20327-b20091230020149 (Java HotSpot(TM) Client VM, Java 1.6.0_17).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def m(v1: Any,v2: Any) = (v1,v2) match {
| case (Some(x),Some(y)) => "a"
| case (Some(_),None) | (None,Some(_)) => "b"
| case _ => "c"
| }
m: (v1: Any,v2: Any)java.lang.String
scala> m(Some(1),Some(2))
res0: java.lang.String = a
scala> m(Some(1),None)
res1: java.lang.String = b
scala> m(None,None)
res2: java.lang.String = c
scala>

You should be able to do it if you define it as a val first:
val MyValThatIsCapitalized = new OneAndOnlyOne(v: Option[Foo] => v.isDefined )
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case MyValThatIsCapitalized(value) => "b"
case _ = > "c"
}
As implied by the name, the name of the val containing the extractor object must be capitalized.

On Scala 2.8:
val result = List(v1,v2).flatten match {
case List(value1, value2) => "a"
case List(value) => "b"
case _ = > "c"
}
On Scala 2.7, however, you need a type hint to make it work. So, assuming value is Int, for instance, then:
val result = (List(v1,v2).flatten : List[Int]) match {
case List(value1, value2) => "a"
case List(value) => "b"
case _ = > "c"
}
The funny thing about it is that I misread "first" as "list" on Mitch Blevins answer, and that gave me this idea. :-)

Since you already matched against (Some(x), Some(y)), you may match against (None, None) explicitly, and the remaining cases are (Some(x), None) and (None, Some(y)):
def decide [T](v1: Option[T], v2:Option[T]) = (v1, v2) match {
case (Some (x), Some (y)) => "a"
case (None, None) => "c"
case _ => "b"
}
val ni : Option [Int] = None
decide (ni, ni) // c
decide (Some (4), Some(3)) // a
decide (ni, Some (3)) // b
decide (Some (4), ni) // b

Related

scala pattern matching on value in sequence of strings

variable someKey can be either "a", "b" or "c".
I can do this:
someKey match {
case "a" => someObjectA.execute()
case "b" => someOther.execute()
case "c" => someOther.execute()
case _ => throw new IllegalArgumentException("Unknown")
}
how can I compress this pattern matching so I can check if someKey with e.g. Seq("b", "c") and if it is in the sequence then replace two lines of pattern match with one?
EDIT:
someKey match {
case "a" => someObjectA.execute()
case someKey if Seq("b","c").contains(someKey) => someOther.execute()
case _ => throw new IllegalArgumentException("Unknown")
}
You can have "or" in the case clause:
someKey match {
case "a" => someObjectA.execute()
case "b"|"c" => someOther.execute()
case _ => ???
}
For this particular case, I'd probably go to
// likely in some companion object so these get constructed once
val otherExecute = { () => someOther.execute() }
val keyedTasks = Map(
"a" -> { () => someObjectA.execute() },
"b" -> otherExecute,
"c" -> otherExecute
)
// no idea on the result type of the execute calls? Unit?
def someFunction(someKey: String) = {
val resultOpt = keyedTasks.get(someKey).map(_())
if (resultOpt.isDefined) resultOpt.get
else throw new IllegalArgumentException("Unknown")
}

pattern matching with case match in scala

I have a match statement like this:
val x = y match {
case array: Array[Float] => call z
case array: Array[Double] => call z
case array: Array[BigDecimal] => call z
case array: Array[_] => show error
}
How do I simplify this to use only two case statements, since first three case statements do same thing, instead of four.
Type erasure does not really gives you opportunity to understand how array was typed. What you should do instead is to extract head ( first element) of array and check it's type. For example following code works for me:
List(1,2,3) match {
case (a:Int) :: tail => println("yep")
}
This work, although not very nice:
def x(y: Array[_]) = y match {
case a if a.isInstanceOf[Array[Double]] ||
a.isInstanceOf[Array[Float]] ||
a.isInstanceOf[Array[BigDecimal]] => "call z"
case _ => "show error"
}
Would have thought that pattern matching with "|" as below would do the trick. However, this gives pattern type is incompatible with expected type on Array[Float] and Array[BigDecimal]. It might be that matching of generic on this single case where it could work has not been given so much attention:
def x(y: Array[_ <: Any]) = y match {
case a # (_:Array[Double] | _:Array[Float] | _:Array[BigDecimal]) => "call z"
case a: Array[_] => "show error"
}
May be it helps a bit:
import reflect.runtime.universe._
object Tester {
def test[T: TypeTag](y: Array[T]) = y match {
case c: Array[_] if typeOf[T] <:< typeOf[AnyVal] => "hi"
case c: Array[_] => "oh"
}
}
scala> Tester.test(Array(1,2,3))
res0: String = hi
scala> Tester.test(Array(1.0,2.0,3.0))
res1: String = hi
scala> Tester.test(Array("a", "b", "c"))
res2: String = oh
You can obtain the class of array elements as follows (it will be null for non-array types): c.getClass.getComponentType. So you can write:
if (Set(classOf[Float], classOf[Double], classOf[BigDecimal]).contains(c.getClass.getComponentType)) {
// call z
} else {
// show error
}
Not particularly Scala'ish, though; I think #thoredge's answer is the best for that.
You could also check whether the Array is empty first and then if not, just pattern match on Array.head...something like:
def x(y: Array[_]) = {
y.isEmpty match {
case true => "error"
case false => y.head match {
case a:Double | a:BigInt => do whatever
case _ => "error"
}
}
}

Pattern matching a BitSet in Scala

Is there an easy/best way to get a BitSet I can pattern match like a list?
val btst = BitSet(1,2,3,4)
btst match {
...
case head :: tail => tail
}
By definition a set is an unordered collection, and pattern matching over such one is error-prone. Convert it to list if you want to... Also, you should not rely on head and tail to always return the same thing.
A BitSet is ordered, but extractorless.
Edit: but not humorless.
object |<| {
def unapply(s: BitSet): Option[(Int, BitSet)] =
if (s.isEmpty) None
else Some((s.head, s.tail))
}
def flags(b: BitSet) = b match {
case f"5 || 10" => println("Five and dime") // alas, never a literal
case 5 |<| any => println(s"Low bit is 5iver, rest are $any")
case i |<| any => println(s"Low bit is $i, rest are $any")
case _ => println("None")
}
def dump(b: BitSet) = println(b.toBitMask.mkString(","))
val s = BitSet(5, 7, 11, 17, 19, 65)
dump(s)
// ordinary laborious tests
s match {
case x if x == BitSet(5) => println("Five")
case x if x == BitSet(5,7,11,17,19,65) => println("All")
case x if x(5) => println("Five or more")
case _ => println("None")
}
// manually matching on the mask is laborious
// and depends on the bit length
s.toBitMask match {
case Array(2L) => println("One")
case Array(657568L) => println("First word's worth")
case Array(657568L, _) => println("All")
case _ => println("None")
}
// or truncate for special case
s.toBitMask(0) match {
case 2L => println("One")
case 657568L => println("First word's worth")
case _ => println("None")
}

Applying or operation to Option results

I have the following code:
class CSplit(var s1: CanvNode, var s2: CanvNode) extends SplitPane
{
topComponent = s1.merge
bottomComponent = s2.merge
def containsV(orig: MapCanvT): Option[MapCanvT] =
{
def containsIn(cn: CanvNode): Option[MapCanvT] = cn match
{
case Left => None
case Right(mc) => if (mc == orig) Some(mc) else None
}
containsIn(s1) match
{
case Some(mc) => Some(mc)
case None => containsIn(s2)
}
}
}
I want to reduce the code of the containsV method. My first thought was to use a fold method to shorten the containsIn method. But Option doesn't have one, nor does it extend Class Either. Shouldn't Option[T] extend Either[T, None] ? Then at least one could use Either's fold method.
My final thought was to treat s1 and s2 as a List and do find over it but I can't get this to compile:
def containsV(orig: MapCanvT):
Option[MapCanvT] = ::[CanvNode](s1, s2).find(_ == Right(orig))
Scala 2.10 adds fold to Option. In the meantime you can use map(f).getOrElse(g) instead:
// These produce identical results
o.fold(g)(x => f(x))
o.map(x => f(x)).getOrElse(g)
Edit: so, for example, the following three do the same thing:
val os: List[Option[Int]] = List(Some(5),None)
// Explicit match
os.map{ _ match {
case Some(x) => x+3
case None => 0
}}
// map+getOrElse
os.map{ _.map(_+3).getOrElse(0) }
// fold
os.map{ _.fold(0)(_+3) }
In the fold case, you give the default value for the None case first, and then the function that handles the case where there is a value. In each case you should get List(8,0).
It can be implemented with a list by using the collectFirst method
def containsV(orig: MapCanvT): Option[MapCanvT]
= List(s1, s2).collectFirst {case i: MapCanvT if (i == (orig) => i}
Let's start with the easy part:
containsIn(s1) match
{
case Some(mc) => Some(mc)
case None => containsIn(s2)
}
is the same as
containsIn(s1) orElse containsIn(s2)
Now we only have to deal with containsIn:
def containsIn(cn: CanvNode): Option[MapCanvT] = cn match
{
case Left => None
case Right(mc) => if (mc == orig) Some(mc) else None
}
We can use fold on Either, which gets rid of most of the pattern matching:
cn.fold(_ => None, Some(_))
But there's the orig thingy. We can handle it with a filter, though:
cn.fold(_ => None, Some(_)) filter (orig.==)
Thus:
def containsV(orig: MapCanvT): Option[MapCanvT] = {
def containsIn(cn: CanvNode): Option[MapCanvT] =
cn.fold(_ => None, Some(_)) filter (orig.==)
containsIn(s1) orElse containsIn(s2)
}
I think orElse is much overlooked.

Pattern matching with more than one match

Consider the following Scala code.
val a = "both"
a match {
case "both" | "foo" => println ("foo") // case 1
case "both" | "bar" => println ("bar") // case 2
}
I would like match to work so that if a == "both", Scala will execute both cases. Is this possible or are there any alternatives to achieve what I want?
Standard pattern-matching will always match on only exactly one case. You can get close to what you want by using the fact that patterns can be treated as partial functions (see the Language Specification, Section 8.5, Pattern Matching Anonymous Functions) and by defining your own matching operator, though:
class MatchAll[S](scrutinee : =>S) {
def matchAll[R](patterns : PartialFunction[S,R]*) : Seq[R] = {
val evald : S = scrutinee
patterns.flatMap(_.lift(evald))
}
}
implicit def anyToMatchAll[S](scrut : =>S) : MatchAll[S] = new MatchAll[S](scrut)
def testAll(x : Int) : Seq[String] = x matchAll (
{ case 2 => "two" },
{ case x if x % 2 == 0 => "even" },
{ case x if x % 2 == 1 => "neither" }
)
println(testAll(42).mkString(",")) // prints 'even'
println(testAll(2).mkString(",")) // prints 'two,even'
println(testAll(1).mkString(",")) // prints 'neither'
The syntax is slightly off the usual, but to me such a construction is still a witness to the power of Scala.
Your example is now written as:
// prints both 'foo' and 'bar'
"both" matchAll (
{ case "both" | "foo" => println("foo") },
{ case "both" | "bar" => println("bar") }
)
(Edit huynhjl pointed out that he gave a frighteningly similar answer to this question.)
At risk of being Captain Obvious, in a case like this it would be simplest just to forget pattern matching and use if.
if (a == "both" || a == "foo") println("foo")
if (a == "both" || a == "bar") println("bar")
If the repetition of a == worries you, you could instead write
if (Set("both", "foo")(a)) println("foo")
if (Set("both", "bar")(a)) println("bar")
using the fact that the apply method on Set does the same as contains, and is a bit shorter.
match executes one, and only one, of the cases, so you can't do this as an or in the match. You can, however, use a list and map/foreach:
val a = "both"
(a match {
case "both" => List("foo", "bar")
case x => List(x)
}) foreach(_ match {
case "foo" => println("foo")
case "bar" => println("bar")
})
And you're not duplicating any of the important code (in this case the printlns).
Just match twice:
val a = "both"
a match {
case "both" | "foo" => println ("foo") // Case 1
}
a match {
case "both" | "bar" => println ("bar") // Case 2
}
One possible way could be:
val a = "both"
a match {
case "foo" => println ("foo") // Case 1
case "bar" => println ("bar") // Case 2
case "both" => println ("foo"); println ("bar")
}