I want to case match tuples which contain boolean values.
Is there an effective way to match the results as case match on boolean looksexhautive
val ABC= "abc"
val DEF = "def"
private def istest1 = true
private def istest2 = true
private def istest3 = true
(istest1, istest2, istest3) match {
case (true, true, true)=>ABC
case (false, true, true) =>DEF
case (false , false , true)=> //print something else
//other cases
}
You need to have a statement for each of the possible outcomes, and using match seems to be the most compact and readable way to do it.
One possible improvement is to use _ for "don't care" values:
(istest1, istest2, istest3) match {
case (true, true, true) => ABC
case (_, true, true) => DEF
case (false, _, true) => //print something
case _ => //other cases
}
There may be performance issues with different versions of these tests, but it is best to pick the one that makes the most sense. Aim for readability and maintainability above perceived performance.
Your approach seems fine, but given all of your values are boolean you could use some stable identifiers to name them and make the code a bit more readable
val ABC = "abc"
val DEF = "def"
def isTest1 = true
def isTest2 = true
def isTest3 = true
val PassedTest1 = true
val PassedTest2 = true
val PassedTest3 = true
val NotPassedTest1 = false
val NotPassedTest2 = false
(isTest1, isTest2, isTest3) match {
case (PassedTest1, PassedTest2, PassedTest3) => ABC
case (NotPassedTest1, _, PassedTest3) => DEF
case (NotPassedTest1, NotPassedTest2, PassedTest3) => //print something
case _ => ???
}
Also as #Tim mentioned, you can use _ to match any value inside the tuple, or to match any tuple as the last case so you don't have to be exaustive
I would even go one step further to Brunos solution:
sealed trait PassedOrNot {
final val Passed = true
final val NotPassed = false
}
case object Test1 extends PassedOrNot
case object Test2 extends PassedOrNot
case object Test3 extends PassedOrNot
def isTest1 = true
def isTest2 = true
def isTest3 = true
(isTest1, isTest2, isTest3) match {
// #formatter:off
case (Test1.Passed , Test2.Passed, Test3.NotPassed) => ???
case (Test1.NotPassed, _ , Test3.NotPassed) => ???
case _ => ???
// #formatter:on
}
Related
With this code println will be executed only for specified exception. I'm wondering if it's possible to negate that line to make it executed for all other exceptions that are not specified. I know it's possible using 2 cases, but I want to know if it can be done with one case.
val myHandler: PartialFunction[Throwable, Unit] = {
case e # (_: MappingException | _: ParseException | _: SomeOtherException) =>
println("Got it")
}
AFAIk you can not do this with a single match, but you can create your own custom Extractor in case you need to replicate this behaviour in multiple places.
import scala.reflect.ClassTag
final class Not[A : ClassTag] {
def unapply(any: Any): Boolean = any match {
case _: A => false
case _ => true
}
}
object Not {
def apply[A : ClassTag]: Not[A] = new Not
}
which you can use like this:
final val NotAnInt = Not[Int]
10 match {
case NotAnInt() => false
case _ => true
}
// res: Boolean = true
"10" match {
case NotAnInt() => true
case _ => false
}
// res: Boolean = true
However, keep in mind this will have all the limitation of any type check, like not being able to differentiate between a List[Int] from a List[String] due erasure; and being considered a bad practice.
I would suggest looking into a typeclass approach, for example, I believe Shapeless provides a negation one.
You can see the code running here.
Well you've already identified what is probably the more readable way to do it.
val myHandler: PartialFunction[Throwable, Unit] = {
case e # (_: MappingException | _: ParseException | _: SomeOtherException) =>
throw e
case _ =>
println("Got it")
}
This is probably how I'd write this in actual production code. It's sensible and it's clear at a glance.
But you asked for one case, so let's give that a go. Since we want to check against several types, we'll need to be able to represent them as a list. There are countless Scala libraries that make this prettier, but for our purposes we'll just roll our own.
trait TList {
def isMember(x: Any): Boolean
}
object Nil extends TList {
def isMember(x: Any) = false
}
case class Cons[H : ClassTag](val tail: TList) extends TList {
def isMember(x: Any) = {
x match {
case _: H => true
case _ => tail.isMember(x)
}
}
}
So we can represent classical Lisp-style singly-linked lists and check whether an arbitrary Any value has a type anywhere in the list. Now let's negate it and write an unapply method.
case class NotMember(val types: TList) {
def unapply(elem: Any): Boolean =
!types.isMember(elem)
}
Then our handler looks like
val test = NotMember(
Cons[MappingException](Cons[ParseException](Cons[SomeOtherException](Nil)))
)
val myHandler: PartialFunction[Throwable, Unit] = {
case test() =>
println("Got it")
}
Again, if you really want to go down this road, you'll want to grab a library to make the type-level stuff manageable. But it's definitely possible. The only question is whether it's worth it for your use case.
I have the following:
def myFunc(str: String): Something => {
str match {
case "a" | "a1" | "abc" | "qwe" | "23rs" => Something
case _ => None
}
}
The string list could be very long, I'd like to extract that to a function. I don't really know what to search for, since doing
def isSomething(str: String): Boolean => {
List("a","a1","abc","qwe","23rs").contains(str)
}
but case isSomething => Something doesn't work
Your str is a String hence won't match isSomething which is of Boolean type. Another issue with your sample code is that None is of Option type, so it would make more sense to have your match cases return the same type. Here's one approach using guard for the contains condition:
val list = List("a", "a1", "abc", "qwe", "23rs")
val s = "abc"
s match {
case s if list contains s => Some(s)
case _ => None
}
// res1: Option[String] = Some(abc)
Most of the other answers seem to cover fixing up the use of option, or moving away from pattern matching (a simple use of guards isn't really pattern matching, IMO)
I think you may be asking about extractors. If so, this might be closer to what you want:
case class Something(str: String)
// define an extractor to match our list of Strings
object MatchList {
def unapply(str: String) = {
str match {
case "a" | "a1" | "abc" | "qwe" | "23rs" => Some(str)
case _ => None
}
}
}
def myFunc(str: String): Option[Something] = {
// use our new extractor (and fix up the use of Option while we're at it)
str match {
case MatchList(str) => Some(Something(str))
case _ => None
}
}
// Couple of test cases...
myFunc("a") // Some(Something(a))
myFunc("b") // None
First you have used wrong => operator while defining the function.
scala> def isSomething(str: String): Boolean = {
| List("a","a1","abc","qwe","23rs").contains(str)
| }
isSomething: (str: String)Boolean
scala> def myFunc(str: String): String = {
|
| str match {
| case str if(isSomething(str)) => "Something"
| case _ => "None"
| }
| }
myFunc: (str: String)String
scala> myFunc("a")
res9: String = Something
I don't know what is something so i have treated it as a string. You could modify it according to your use case.
Hope it helps.
You can do something like below
val list = List("a", "a1", "abc", "qwe", "23rs")
def myFunc(str: String): Option[String] = {
list.contains(str) match {
case true => ??? //calling something function should return Some
case false => None
}
}
Option[String] can be changed according to the return type you have , but None suggests that the true case should return Option type . So String can be changed to your desired datatype
I'd like to ask where def macros can be called and when they are expanded? I guess we cant just put an appropriate generated AST anywhere it fits?
For example, I want this:
(2,1) match {
case StandaloneMacros.permutations(1,2) => true ;
case (_,_) => false
}
become this after macro expansion
(2,1) match {
case (1,2) | (2,1) => true ;
case (_,_) => false
}
My macro permutations produces an Alternative of tuples. But when I run the first snippet, I get
macro method permutations is not a case class, nor does it have an unapply/unapplySeq member
I also tried defining a Permutations object with unapply macro method but got another error:
scala.reflect.internal.FatalError: unexpected tree: class scala.reflect.internal.Trees$Alternative
So: Is it possible to achieve at all?
I came up with a solution some time ago, and I thought I'd share it with you.
To achieve above task I've used a Transformer and transformCaseDefs
object Matcher {
def apply[A, B](expr: A)(patterns: PartialFunction[A, B]): B = macro apply_impl[A,B]
def apply_impl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(expr: c.Expr[A])(patterns: c.Expr[PartialFunction[A, B]]): c.Expr[B] = {
import c.universe._
def allElemsAreLiterals(l: List[Tree]) = l forall {
case Literal(_) | Ident(_) => true
case _ => throw new Exception("this type of pattern is not supported")
}
val transformer = new Transformer {
override def transformCaseDefs(trees: List[CaseDef]) = trees.map {
case cas # CaseDef(pat # Apply(typTree, argList), guard, body) if allElemsAreLiterals(argList) =>
val permutations = argList.permutations.toList.map(t => q"(..$t)").map {
case Apply(_, args) => Apply(typTree, args)
}
val newPattern = Alternative(permutations)
CaseDef(newPattern, guard, body)
case x => x
}
}
c.Expr[B](q"${transformer.transform(patterns.tree)}($expr)")
}
}
It would be shorter, but somehow you need to provide the same TypeTree which was used in original (before transformation) case statement.
This way, it can be used like this
val x = (1,2,3)
Matcher(x) {
case (2,3,1) => true
case _ => false
}
which is then translated to something like
val x = (1,2,3)
x match {
case (1,2,3) | (1,3,2) | (2,1,3) | (2,3,1) | (3,1,2) | (3,2,1) => true
case _ => false
}
I have created a class called CaseInsensitive which wraps a string (see Implementing a string class that does case insensitive comparisions in Scala).
I've created a case class which has a member variable of type CaseInsensitive, so it gets a default unapply method, which extracts a variable of type CaseInsensitive, but I was hoping to use it like this:
case class PropertyKey( val name : CaseInsensitive )
val foo = new PropertyKey("foo")
val result = foo match {
case PropertyKey("foo") => true
case _ => false
}
This code fails to compile: (on the extractor line, not the constructor line)
type mismatch;
found : java.lang.String("foo")
required: com.acme.CaseInsensitive
But I thought my implicit conversions from String to CaseInsensitive would enable this to compile, rather than me having to type the more verbose:
case class PropertyKey( val name : CaseInsensitive )
val foo = new PropertyKey("foo")
val result = foo match {
case PropertyKey(CaseInsensitive("foo")) => true
case _ => false
}
Here is the implementation of CaseInsensitive:
/** Used to enable us to easily index objects by string, case insensitive
*
* Note: this class preserve the case of your string!
*/
case class CaseInsensitive ( val _s : String ) extends Proxy {
require( _s != null)
val self = _s.toLowerCase
override def toString = _s
def i = this // convenience implicit conversion
}
object CaseInsensitive {
implicit def CaseInsensitive2String(c : CaseInsensitive) = if ( c == null ) null else c._s
implicit def StringToCaseInsensitive(s : String) = CaseInsensitive(s)
def fromString( s : String ) = s match {
case null => None
case _ => Some(CaseInsensitive(s))
}
}
You could always define a convenience extractor and import it (feel free to use a shorter name):
object PropertyKeyCI {
def unapply(p: PropertyKey): Option[String] = Some(p.name.self)
}
Then, extraction is:
val foo = new PropertyKey("foo")
val result = foo match {
case PropertyKeyCI("foo") => true
case _ => false
}
(Bad Semantics Alert)
Although note that this would match as false for PropertyKeyCI("Foo"), because your "CaseInsensitive" class is really a "LowerCase" class. I say this because it is hard for me to imagine what the desired behavior would be for the unapply() method otherwise. From your case class default, you are returning an Option[String] of the original (unlowercased) string, which gives this unintuitive behavior:
// result == false !!!!
val foo = new CaseInsensitive("Foo")
val result = foo match {
case CaseInsensitive("foo") => true
case _ => false
}
val CaseInsensitive(s) = "aBcDeF"
assertFalse(s == "abcdef")
Awwww.... toss it. Just use DOS.
I tried to create an unapply method to use in pattern matching, and I tried to make it return something different than Option, however, Eclipse shows that as an error. Is it a rule that unapply must return an Option[T] ?
EDIT: here's the code I'm trying to use. I switched the code from the previous section so that unapply returns a Boolean
import java.util.regex._
object NumberMatcher {
def apply(x:String):Boolean = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return matcher.find
}
def unapply(x:String):Boolean = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return matcher.find
}
}
object x {
def main(args : Array[String]) : Unit = {
val strings = List("geo12","neo493","leo")
for(val str <- strings) {
str match {
case NumberMatcher(group) => println(group)
case _ => println ("no")
}
}
}
}
Eclipse says wrong number of arguments for object NumberMatcher. Why is that?
If you want to return something with unapply, return it inside Some. Returning Boolean just tests if the match can be made or not.
Here is how a pattern matching is translated:
str match {
case NumberMatcher(group) => println(group)
case _ => println("no")
}
Assuming NumberMatcher returns Option[...], it will do:
val r1 = NumberMatcher.unapply(str)
if (r1 != None) {
val group = r1.get
println(group)
} else {
println("no")
}
If NumberMatcher returns Boolean, then you can have it receive something. In that case, this is what happens:
str match {
case NumberMatcher() => println("yes")
case _ => println("no")
}
becomes
val r1 = NumberMatcher.unapply(str)
if (r1) {
println("yes")
} else {
println("no")
}
Note that this is a very superficial explanation. Case matches can test for constants, have additional guard conditions, alternatives, use unapply recursively, use unapplySeq, etc. Here I'm only showing very basic usage to address a specific question. I strongly advise searching for a fuller explanation of pattern matching.
Take a look at this example again.
I quote
The return type of an unapply should be chosen as follows:
* If it is just a test, return a Boolean. For instance case even()
* If it returns a single sub-value of type T, return a Option[T]
* If you want to return several sub-values T1,...,Tn, group them in an optional tuple Option[(T1,...,Tn)].
When you defined unapply to return a Boolean, you were indicating that the pattern doesn't have any wildcards to match (or bind). So the case statement for this unextractor should be case NumberMatcher => println(str), and giving it a variable to fill is wrong.
Alternatively, to make case NumberMatcher(group) => println(group) you need to define unapply() to return Option[String]
package com.tutorial.extracters
object ExtracterwithBooleanReturnType extends App {
import java.util.regex._
object NumberMatcher {
def apply(x: String*) = {
x
}
def unapply(x: String): Option[Boolean] = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return Some(matcher.find)
}
}
val strings = NumberMatcher("geo12", "neo493", "leo")
for (str <- strings) {
str match {
case NumberMatcher(group) => println(group)
case _ => println("no")
}
}
}
we can achieve this with above code as well