I have the following scala object definition:
case class RecursiveSettings (
var recursiveFrom: Int,
var recursiveTo: Int,
var nonRecursiveFrom: Int,
var nonRecursiveTo: Int,
var betweenReach: Int,
var scoresamephrase: Boolean
)
and I'm trying to get the variables from an ArrayBuffer:
import scala.collection.mutable.ArrayBuffer
def main(args: Array[String]){
var settings = List("","1", "2", "0", "0", true)
var newsettings = new ArrayBuffer[Any]
println(settings)
settings.foreach {a =>
val z = a match{
case "" => 0
case s : String => s.toInt
case _ => a
}
newsettings += z
}
println(newsettings)
var result = new RecursiveSettings(0,0,0,0,0,true)
println(result)
for (i <- 0 to (newsettings.length - 1)){
println("newsettings_i", newsettings(i))
val x = newsettings(i) match{
case y : Int => y
case y : Boolean => y
case _ => 0
}
println("x:", x)
i match{
case 0 => result.recursiveFrom = x
case 1 => result.recursiveTo = x
case 2 => result.nonRecursiveFrom = x
case 3 => result.nonRecursiveTo = x
case 4 => result.betweenReach = x
case 5 => result.scoresamephrase = x
}
}
}
If I comment out the i match statement and do a simple type match:
for (i <- 0 to (newsettings.length - 1)){
println("newsettings_i", newsettings(i))
val x = newsettings(i) match{
case y : Int => "Int"
case y : Boolean => "Bool"
case _ => 0
}
println("x:", x)
the code compiles, runs and I get:
List(, 1, 2, 0, 0, true)
ArrayBuffer(0, 1, 2, 0, 0, true)
RecursiveSettings(0,0,0,0,0,true)
(newsettings_i,0)
(x:,Int)
(newsettings_i,1)
(x:,Int)
(newsettings_i,2)
(x:,Int)
(newsettings_i,0)
(x:,Int)
(newsettings_i,0)
(x:,Int)
(newsettings_i,true)
(x:,Bool)
But when I add the i match statement back in I get a lot of complaints of this type:
~/match.scala:44: error: type mismatch;
found : AnyVal
required: Int
case 0 => result.recursiveFrom = x
Can someone please help me to understand:
Why does the simple type match produce the desired result, but this is not carried through to the object?
What can I do to correct my code?
Thanks in advance, this has had me banging my head for hours!
EDIT
Ok so building on the info from #Alex Savitsky and #Jakub Zalas (thanks guys) I've substantially modified the original code to what I hope is something more functionally orientated which can deal with mixed init value types:
object matcher2{
def main(args: Array[String]):Unit = {
val init = Array("",1, "4", null, "0", false)
matchf(init)
}
def matchf(args : Array[_] ) : RecursiveSettings = {
val settings : RecursiveSettings = args.map{
case "" => 0
case "true" => true
case "false" => false
case b : Boolean => b
case s : String => s.toInt
case i : Int => i
case null => 0
} match {
case Array(recursiveFrom: Int, recursiveTo: Int, nonRecursiveFrom: Int, nonRecursiveTo: Int, betweenReach: Int, scoresamephrase: Boolean) =>
RecursiveSettings(recursiveFrom, recursiveTo, nonRecursiveFrom, nonRecursiveTo, betweenReach, scoresamephrase)
}
println(settings)
settings
}
}
Being new to Scala (and Java) from Python I'm still struggling a fair bit with the functional and static typing aspects, so any comments / suggestions gratefully received.
Thanks for your help.
You can convert the arguments to the appropriate types on the fly, and then match on the whole collection at once:
// assuming your args is an array of ["", "1", "2", "0", "0", "true"]
val settings: RecursiveSettings = args.map {
case "" => 0
case "true" => true
case "false" => false
case s: String => s.toInt
} match {
case Array(recursiveFrom: Int, recursiveTo: Int, nonRecursiveFrom: Int, nonRecursiveTo: Int, betweenReach: Int, scoresamephrase: Boolean) =>
RecursiveSettings(recursiveFrom, recursiveTo, nonRecursiveFrom, nonRecursiveTo, betweenReach, scoresamephrase)
}
You can also match on the partially provided arguments, as long as you decide which parameters will receive the default values (this case can be used alongside the "full" case match):
case Array(recursiveFrom: Int, recursiveTo: Int) =>
RecursiveSettings(recursiveFrom, recursiveTo, 0, 2, 1, true)
Pattern matching you defined doesn't resolve to a single type:
val x = newsettings(i) match {
case y : Int => y
case y : Boolean => y
case _ => 0
}
The result can be either Int or Boolean, therefore the type of x will be AnyVal as Scala cannot infer a single type.
Quick (and dirty) solution
Probably the simplest way to fix your code without modifying it much, is to explicitly cast x to the expected type:
i match {
case 0 => result.recursiveFrom = x.asInstanceOf[Int]
case 1 => result.recursiveTo = x.asInstanceOf[Int]
case 2 => result.nonRecursiveFrom = x.asInstanceOf[Int]
case 3 => result.nonRecursiveTo = x.asInstanceOf[Int]
case 4 => result.betweenReach = x.asInstanceOf[Int]
case 5 => result.scoresamephrase = x.asInstanceOf[Boolean]
}
Better solution
I suggest you try to redesign your code with immutable data structures and try to use a more functional approach to solve your problem. This way your code will be more readable and less prone to side effects.
For example, the way you create newsettings from settings could be simplified a lot:
val settings = List("","1", "2", "0", "0", true)
val newsettings = settings map {
case "" => 0
case s : String => s.toInt
case a => a
}
i have a question about the Parser in Scala. I'll just post the important part here so that there would not be too much codes. For the eval Function:
def eval(t: Term): Double = t match {
case Addition(l, r) => eval(l) + eval(r)
case Multiplication(l, r) => eval(l) * eval(r)
case Numeric(i) => i
case Variable("X") => 3
}
And the calculate function:
def calculate(arg: String): Double = {
return eval(parseAll(term, arg).get)
}
now i should overload the function "calculate" so that it takes an extra Parameter tup : (String, Double) and assign the value for this String. For example ("Y",2) then Y = 2 in the Parser. And then calculate the parser. But i don't know how to assign the value for this String. I had a stupid idea and tried this but it didn't work.
def calculate(arg: String, tup : (String, Double)) : Double = {
tup match {
case (a,b) => {
def eval(t : Term): Double = t match {
case Variable(a) => b
}
return eval(parseAll(term, arg).get)
}
}
can you guys pls help me out ? Thank you !!
You're almost there, you just need to tell the compiler that the a in your Variable pattern is actually the a from your (a, b) pattern. By default, what you do is called shadowing of the variable name a (in the scope of this pattern match, a is the value extracted in Variable, and the other a is forgotten).
What you want is something like
...
case Variable(`a`) => b
...
or, if your expression gets a little more complicated, you should rather use a guard:
...
case Variable(v) if v == a => b
...
EDIT However, now your eval function is not well defined. You need to put it all at once:
def eval(t: Term, varAssignement: (String, Double)): Double = t match {
case Addition(l, r) => eval(l) + eval(r)
case Multiplication(l, r) => eval(l) * eval(r)
case Numeric(i) => i
case Variable(a) if a == varAssignment._1 => varAssignment._2
}
Or, if you want to have multiple variables:
def eval(t: Term, assignments: Map[String, Double]): Double = t match {
case Addition(l, r) => eval(l) + eval(r)
case Multiplication(l, r) => eval(l) * eval(r)
case Numeric(i) => i
case Variable(a) if assignments.exists(a) => assignments(a)
}
Beware that you'll still get MatchErrors whenever an unassigned variable is used.
I am writing a Hive UDF in Scala (because I want to learn scala). To do this, I have to override three functions: evaluate, initialize and getDisplayString.
In the initialize function I have to:
Receive an array of ObjectInspector and return an ObjectInspector
Check if the array is null
Check if the array has the correct size
Check if the array contains the object of the correct type
To do this, I am using pattern matching and came up with the following function:
override def initialize(genericInspectors: Array[ObjectInspector]): ObjectInspector = genericInspectors match {
case null => throw new UDFArgumentException(functionNameString + ": ObjectInspector is null!")
case _ if genericInspectors.length != 1 => throw new UDFArgumentException(functionNameString + ": requires exactly one argument.")
case _ => {
listInspector = genericInspectors(0) match {
case concreteInspector: ListObjectInspector => concreteInspector
case _ => throw new UDFArgumentException(functionNameString + ": requires an input array.")
}
PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(listInspector.getListElementObjectInspector.asInstanceOf[PrimitiveObjectInspector].getPrimitiveCategory)
}
}
Nevertheless, I have the impression that the function could be made more legible and, in general, prettier since I don't like to have code with too many levels of indentation.
Is there an idiomatic Scala way to improve the code above?
It's typical for patterns to include other patterns. The type of x here is String.
scala> val xs: Array[Any] = Array("x")
xs: Array[Any] = Array(x)
scala> xs match {
| case null => ???
| case Array(x: String) => x
| case _ => ???
| }
res0: String = x
The idiom for "any number of args" is "sequence pattern", which matches arbitrary args:
scala> val xs: Array[Any] = Array("x")
xs: Array[Any] = Array(x)
scala> xs match { case Array(x: String) => x case Array(_*) => ??? }
res2: String = x
scala> val xs: Array[Any] = Array(42)
xs: Array[Any] = Array(42)
scala> xs match { case Array(x: String) => x case Array(_*) => ??? }
scala.NotImplementedError: an implementation is missing
at scala.Predef$.$qmark$qmark$qmark(Predef.scala:230)
... 32 elided
scala> Array("x","y") match { case Array(x: String) => x case Array(_*) => ??? }
scala.NotImplementedError: an implementation is missing
at scala.Predef$.$qmark$qmark$qmark(Predef.scala:230)
... 32 elided
This answer should not be construed as advocating matching your way back to type safety.
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.
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)
}