I feel like this is a silly question, but I'll ask anyway... I'm trying to do something like this:
def func(x: Int, y: Int) = {
val value: Int = 0 //from config
(x, y) match {
case (value, value) => "foo"
case _ => "bar"
}
}
But both the repl and intelliJ shower me with warnings. (e.g. "patterns after a variable pattern cannot match"; "suspicious shadowing by a variable pattern"; etc.). Is there a correct way to match on non-literal values?
Yes! There are two ways to get what you want. The first is to capitalize the names of the variables you wish to match against:
def func(x: Int, y: Int) = {
val Value: Int = 0 // from config
(x, y) match {
case (Value, Value) => "foo"
case _ => "bar"
}
}
If you don't want to go that route (because it's not idiomatic to capitalize variable names, etc.), you can backtick them in the match:
def func(x: Int, y: Int) = {
val value: Int = 0 // from config
(x, y) match {
case (`value`, `value`) => "foo"
case _ => "bar"
}
}
I'd suggest using backticks in most situations.
Related
I have a usage case like;
val fieldType = "int" // we know what is the type of field
val value1 = "1" // can be double/int/long/float etc.
val value2 = "2" // can be double/int/long/float etc.
val operator = "=" // can be >, >=, <, <= etc.
def compare(operator, value1, value2): Boolean = {}
Couldn't find a good way to do this. Is there any way to do this efficiently?
EDIT
I've just made this one but not sure this is the most efficient way;
private def apply(fieldType: String, operator: String, value1: String, value2: String): Boolean =
operator match {
case ">" =>
compare(fieldType, value1, value2) > 0
case ">=" =>
compare(fieldType, value1, value2) >= 0
case "<" =>
compare(fieldType, value1, value2) < 0
case "<=" =>
compare(fieldType, value1, value2) <= 0
case "==" =>
compare(fieldType, value1, value2) == 0
}
private def compare(fieldType: String, value1: String, value2: String): Int =
fieldType match {
case "string" => value1.compareTo(value2)
case "int" => value1.toInt.compareTo(value2.toInt)
case "long" => value1.toLong.compareTo(value2.toLong)
case "float" => value1.toFloat.compareTo(value2.toFloat)
}
The advantage of this solution is that it's generic, so it will work for all numeric types.
I'm using the implicit ordering that Scala provides for every numeric type. And I provide a generic compare method that works for very numeric type. But I still had to parse fieldType to figure out what types I should compare.
T : Ordering means a context bound - this avoids the explicit use of an implicit parameter in the method, and lets Scala provide one of type Ordering[T] automatically wherever I need it in the body of the method.
implicitly[Ordering[T] is a standard library method that tells Scala to look for an implicit definition of type Ordering[T], call it, and return the object right back, then use the methods defined in trait Ordering for comparation purposes.
These 2 ideas fuse together to eliminate the need to include an implicit parameter for the method, because we are not interested in an explicit ordering parameter:
def compareBy(fieldType: String)(
op: String,
a: String,
b: String
): Boolean = {
def compare[T: Ordering](op: String, a: T, b: T): Boolean =
op match {
case "==" => implicitly[Ordering[T]].eq(a, b)
case "<" => implicitly[Ordering[T]].lt(a, b)
case "<=" => implicitly[Ordering[T]].lteq(a, b)
case ">" => implicitly[Ordering[T]].gt(a, b)
case ">=" => implicitly[Ordering[T]].gteq(a, b)
case _ => throw new UnsupportedOperationException("op not supported!")
}
fieldType match {
case "byte" => compare(op, a.toByte, b.toByte)
case "short" => compare(op, a.toShort, b.toShort)
case "int" => compare(op, a.toInt, b.toInt)
case "long" => compare(op, a.toLong, b.toLong)
case "float" => compare(op, a.toFloat, b.toFloat)
case "double" => compare(op, a.toDouble, b.toDouble)
case _ =>
throw new UnsupportedOperationException("fieldType not supported!")
}
}
val a = "10"
val b = "2"
val op = ">"
val intFieldType = "int"
val compareInts = compareBy(intFieldType) _
println(compareInts(op, a, b)) // true
The solution seems over-engineered. The following observations can lead to a more compact one:
we can treat any number as a Double, because no precision is lost from converting the smaller type Float to Double or from converting integral types to Double.
in the corner-case where we want to compare 2 numbers given as floating-point string literals using an integral type, we drop the precision using longValue, then convert them back to Double, to remove the need of a generic method. By using toDoubleOption first, we also remove the NumberFormatException when parsing a floating-point string literal as an integral type.
def compareBy(
fieldType: String
)(op: String, a: String, b: String): Boolean = {
def compare(op: String, a: Double, b: Double): Boolean = {
op match {
case "==" => a == b
case "<" => a < b
case "<=" => a <= b
case ">" => a > b
case ">=" => a >= b
case _ => throw new UnsupportedOperationException("op not supported!")
}
}
val x: Double = a.toDoubleOption.getOrElse(
throw new NumberFormatException(s"given $a is not parsable as $fieldType")
)
val y: Double = b.toDoubleOption.getOrElse(
throw new NumberFormatException(s"given $b is not parsable as $fieldType")
)
fieldType match {
case "byte" | "short" | "int" | "long" =>
compare(op, x.longValue.toDouble, y.longValue.toDouble)
case "float" | "double" => compare(op, x, y)
case _ =>
throw new UnsupportedOperationException("fieldType not supported!")
}
}
val a = "10.1"
val b = "10.2"
val op = "=="
val intFieldType = "int"
val compareInts = compareBy(intFieldType) _
println(compareInts(op, a, b)) // true
I'm trying to do something like:
private val isOne = (x: Int) => x == 1
private val isTwo = (x: int) => x == 2
def main(x: Int): String = {
x match {
case isOne => "it's one!"
case isTwo => "it's two!"
case _ => ":( It's not one or two"
}
}
Unfortunately... doesn't look like my syntax is right or maybe that's just no possible in Scala... any suggestions?
This isn't going to work for two reasons. First,
case isOne => ...
is not what you think it is. isOne within the match is just a symbol that will eagerly match anything, and not a reference to the val isOne. You can fix this by using backticks.
case `isOne` => ...
But this still won't do what you think it does. x is an Int, and isOne is a Int => Boolean, which means they will never match. You can sort of fix it like this:
def main(x: Int): String = {
x match {
case x if(isOne(x)) => "it's one!"
case x if(isTwo(x)) => "it's two!"
case _ => ":( It's not one or two"
}
}
But this isn't very useful, and case 1 => .... does the job just fine.
As an example, we define a function that should convert 1, 3, 42 respectively to "foo", "bar", "qix" and all other integer to "X".
I've come up with 2 implementations :
The method f need to be separate because it can be reuse in other context.
def f(i: Int): Option[String] = i match {
case 1 => Some("foo")
case 3 => Some("bar")
case 42 => Some("qix")
case _ => None
}
def g(i: Int) : String = f(i).getOrElse("X")
And :
def f_ : PartialFunction[Int, String] = {
case 1 => "foo"
case 3 => "bar"
case 42 => "qix"
}
def g_(i: Int) : String = f_.orElse { case _ => "X" }(i)
I tend to prefer the second because it avoid many repetitive Some(…)
WDYT ?
I'm not sure why you want to use option at all when you can just as easily do this and get the exact same result:
def f(i: Int): String = i match {
case 1 => "foo"
case 3 => "bar"
case 42 => "qix"
case _ => "X"
}
It even saves you a pesky getOrElse
You can even go one better and use neither a PartialFunction or a match and just do this:
def f: Int => String = {
case 1 => "foo"
case 3 => "bar"
case 42 => "qix"
case _ => "X"
}
Which saves you writing a disposable i
fScala's Map is already a partial function. So you can use it instead of defining your own function which does exactly what Map does - "A map from keys of type A to values of type B".
So all you have to do is:
val f = Map(1 -> "foo", 3 -> "bar", 42 -> "qix")
def g(i: Int) = f.getOrElse(i, "X")
f(1) //foo
f(4) // throw NoSuchElementException: Key not found: 4
f.get(1) // Some(foo)
f.get(4) // None
g(1) //foo
g(4) //X
Now you can use the function 'g' or reuse 'f' to other needs.
Edited my example according to your comment:
def f(i: Int): Option[String] = {
val map = Map(1 -> "foo", 3 -> "bar", 42 -> "qix")
i match {
case x if (map.contains(x)) => Some(map(x))
case _ => None
}
}
def g(i: Int) : String = f(i).getOrElse("X")
I think the function should react to integers outside the given range in some meaningful way. That's why I would prefer an Option.
Option is a good way to handle null value. Partial function is just a partial matching, they are not same, even though Option and PartialFunction both have similar orElse method
Due to partial function is a function, so it can be chained but option can not, Option is the way to handle null value.
For the partial function you can do like this, it is more like chain of responsibility
def f_1 : PartialFunction[Int, String] = {
case 1 => "1"
}
def f_2 : PartialFunction[Int, String] = {
case 2 => "2"
}
def f_3 : PartialFunction[Int, String] = {
case 3 => "3"
}
f_1.orElse(f_2).orElse(f_3)(3)
You may want to try this. Here HashMap gives you fast lookup:
object SomeMain {
import scala.collection.immutable.HashMap
def f(i: Int): Option[String] = {
HashMap(1 -> "foo", 3 -> "bar", 42 -> "qix").get(i).orElse(None)
}
def g(i: Int): String = f(i).getOrElse("X")
def main(args: Array[String]) {
List(1, 3, 42, 10) foreach { x => println(x + ": " + g(x)) }
}
}
Output:
1: foo
3: bar
42: qix
10: X
Say I have the following
case class IntWrap(value:Int)
I would like to extract the same variable from two cases as follows:
x match {
case value:Int | IntWrap(value) => dosomethingwith(x)
case _ => ???
}
but the only way I have been able to do this is as:
x match {
case value:Int => dosomethingwith(x)
case IntWrap(value) => dosomethingwith(x)
case _ => ???
}
Is there a better way, as in my real life case dosomething is actually a large block of code which is not so easy to encapsulate.
If it is really the case that you want to do something with x, not with the extracted value, then the following would work:
case class IntWrap(value:Int) // extends T
def dosomethingwith(x: Any) = x
val x: Any = IntWrap(101)
x match {
case _: Int | _: IntWrap => dosomethingwith(x)
case _ => ???
}
If you actually want to work with the extracted value, you could factor out the corresponding match block into its own extractor and reuse that wherever necessary:
x match {
case Unwrap(value) => dosomethingwith(value)
case _ => ???
}
object Unwrap {
def unapply(x: Any) = x match {
case x: Int => Some((x))
case IntWrap(value) => Some((value))
case _ => None
}
}
I honestly don't see an issue with the way you are doing things. As long as dosomethingwith is a separate function then I don't see any issues with duplicate code. If your code looked like this then I don't see any need to come up with other solutions:
def foo(x:Any){
x match {
case value:Int => dosomethingwith(value)
case IntWrap(value) => dosomethingwith(value)
case _ => ???
}
}
def dosomethingwith(x:Int){
//do something complicated here...
}
I came up with sth a little bit different, but it may help you avoid duplicates:
case class IntWrap(value: Int)
implicit def intWrapToInt(intWrap: IntWrap) = intWrap.value
def matchInt(x: AnyVal) = x match {
case i: Int => println("int or intWrap")
case _ => println("other")
}
//test
matchInt(IntWrap(12)) //prints int or intWrap
matchInt(12) //prints int or intWrap
matchInt("abc") //prints other
It won't work for every reference, though. So, be careful.
If you have a pattern matching (case) in Scala, for example:
foo match {
case a: String => doSomething(a)
case f: Float => doSomethingElse(f)
case _ => ? // How does one determine what this was?
}
Is there a way to determine what type was actually caught in the catch-all?
case x => println(x.getClass)
Too easy :-)
Basically, you just need to bind the value in your catch-all statement to a name (x in this case), then you can use the standard getClass method to determine the type.
If you're trying to perform specific logic based on the type, you're probably doing it wrong. You could compose your match statements as partial functions if you need some 'default' cases that you don't want to define inline there. For instance:
scala> val defaultHandler: PartialFunction[Any, Unit] = {
| case x: String => println("String: " + x)
| }
defaultHandler: PartialFunction[Any,Unit] = <function1>
scala> val customHandler: PartialFunction[Any, Unit] = {
| case x: Int => println("Int: " + x)
| }
customHandler: PartialFunction[Any,Unit] = <function1>
scala> (customHandler orElse defaultHandler)("hey there")
String: hey there
foo match {
case a: String => doSomething(a)
case f: Float => doSomethingElse(f)
case x => println(x.getClass)
}