how to test for the length of a var arg parameter for a contructor.
I am defining a case class polygon which takes in a sequence of points, I want to make sure that the no of points is atleast 5 usinga require clause.
case class Point(x: Int, y: Int)
case class Polygon(points: Point*) {
// require(point >= 3) }
How about this?:
case class Point(x: Int, y: Int)
case class Polygon(a: Point, b: Point, c: Point, d: Point, e: Point, other: Point*) {
def points = Vector(a,b,c,d,e) ++ other
}
Then:
val p1 = Point(1,1)
val p2 = Point(2,1)
val p3 = Point(3,1)
val p4 = Point(4,1)
val p5 = Point(5,1)
val p6 = Point(6,1)
val p7 = Point(7,1)
val polygon5 = Polygon(p1,p2,p3,p4,p5)
println(polygon5.points)
// Vector(Point(1,1), Point(2,1), Point(3,1), Point(4,1), Point(5,1))
val polygon7 = Polygon(p1,p2,p3,p4,p5,p6,p7)
println(polygon7.points)
// Vector(Point(1,1), Point(2,1), Point(3,1), Point(4,1), Point(5,1), Point(6,1), Point(7,1))
Polygon(p1,p2,p3,p4) // error: not enough arguments for method apply
Moving this requirement to compile time by making the class take in 5 Point arguments and then a Point* variadic argument is usually going to be your best bet (as shown by dhg's answer).
If you want to use require instead, it is quite simple:
case class Polygon(points: Point*) {
require(points.length >= 5, "Must have at least 5 points")
}
Related
as mentioned in the title I have a SortedSet with custom ordering. The set holds objects of class Edge (representing an edge in a graph). Each Edge has a cost associated with it as well as it's start and end point.
case class Edge(firstId : Int, secondId : Int, cost : Int) {}
My ordering for SortedSet of edges looks like this (it's for the A* algorithm) :
object Ord {
val edgeCostOrdering: Ordering[Edge] = Ordering.by { edge : Edge =>
if (edge.secondId == goalId) graphRepresentation.calculateStraightLineCost(edge.firstId, goalId) else edge.cost + graphRepresentation.calculateStraightLineCost(edge.secondId, goalId)
}
}
However after I apply said ordering to the set and I try to sort edges that have different start/end points but the same cost - only the last encountered edge retains in the set.
For example :
val testSet : SortedSet[Edge] = SortedSet[Edge]()(edgeOrder)
val testSet2 = testSet + Edge(1,4,2)
val testSet3 = testSet2 + Edge(3,2,2)
println(testSet3)
Only prints (3,2,2)
Aren't these distinct objects? They only share the same value for one field so shouldn't the Set be able to handle this?
Consider using a mutable.PriorityQueue instead, it can keep multiple elements that have the same order. Here is a simpler example where we order pairs by the second component:
import collection.mutable.PriorityQueue
implicit val twoOrd = math.Ordering.by{ (t: (Int, Int)) => t._2 }
val p = new PriorityQueue[(Int, Int)]()(twoOrd)
p += ((1, 2))
p += ((42, 2))
Even though both pairs are mapped to 2, and therefore have the same priority, the queue does not lose any elements:
p foreach println
(1,2)
(42,2)
To retain all the distinct Edges with the same ordering cost value in the SortedSet, you can modify your Ordering.by's function to return a Tuple that includes the edge Ids as well:
val edgeCostOrdering: Ordering[Edge] = Ordering.by { edge: Edge =>
val cost = if (edge.secondId == goalId) ... else ...
(cost, edge.firstId, edge.secondId)
}
A quick proof of concept below:
import scala.collection.immutable.SortedSet
case class Foo(a: Int, b: Int)
val fooOrdering: Ordering[Foo] = Ordering.by(_.b)
val ss = SortedSet(Foo(2, 2), Foo(2, 1), Foo(1, 2))(fooOrdering)
// ss: scala.collection.immutable.SortedSet[Foo] = TreeSet(Foo(2,1), Foo(1,2))
val fooOrdering: Ordering[Foo] = Ordering.by(foo => (foo.b, foo.a))
val ss = SortedSet(Foo(2, 2), Foo(2, 1), Foo(1, 2))(fooOrdering)
// ss: scala.collection.immutable.SortedSet[Foo] = TreeSet(Foo(1,2), Foo(2,1), Foo(2,2))
Given
case class Foo (
x: Int = 1,
y: String,
)
What is the best way to instantiate said class, overwriting default params only if a local condition is fulfilled (e.g. the local variable corresponding to the constructor parameter is not None)
object Test {
/* Let's pretend I cannot know the state of x,y
* because they come from the network, a file... */
val x: Option[Int] = getX()
val y: Option[String] = getY()
Foo(
x=???, // how to get x=if(x.isDefined) x else "use default of Foo" here
y=y.get,
)
}
The straightforward solution of checking the condition and instantiating the case class differently does not scale (seven arguments with default values -> 128 different instantiations)
Solution 1) One solution I know is:
object Foo {
val Defaults = Foo(y="")
}
object Test {
val x: Option[Int] = getX()
val y: Option[String] = getY()
Foo(
x=x.getOrElse(Foo.Defaults.x)
y=y.get
)
}
This works ok-ish. When y is None I get the NoSuchElementException, but that's OK because it is a mandatory constructor argument. However, this is clearly a hack and has the distinct drawback that it is possible to write:
Foo(
x=x.getOrElse(Foo.Defaults.x)
y=y.getOrElse(Foo.Defaults.y)
)
When y is None you get a non-sensical default value for y.
Solution 2) Another solution is something like:
sealed trait Field
case object X extends Field
object Foo {
private val template = Foo(y="")
val Defaults = {
case X => template.x
}
}
object Test {
val x: Option[Int] = getX()
val y: Option[String] = getY()
Foo(
x=x.getOrElse(Foo.Defaults(X))
y=y.get
)
}
This is a bit better type safety-wise, but now I have to create a type for each default parameter.
What would a correct and concise solution look like?
Not clear to me how you can do better than the following:
object Foo { def apply(optX: Option[Int], optY: Option[String]): Foo = Foo(optX.getOrElse(1), optY.get) }
case class Foo private(x: Int, y: String)
Foo(Some(5), None) // fails with NoSuchElementException
Foo(Some(5), Some("Hi")) // succeeds in creating Foo(5, "Hi")
Foo(None, Some("Hi")) // succeeds in creating Foo(1, "Hi"), note the default value of x
Whether a parameter is required or optional with a default is encoded via the invocation of get for the former and getOrElse for the latter.
Of course, you could wrap the Option's get method to provide a more meaningful message when required parameters are omitted.
I realize that is not far from your solution 1 and may not meet your needs.
As a follow up of
Matt R's question, as Scala 2.10 has been out for quite an amount of time, what would be the best way to extract the fields and values of a case class. Taking a similar example:
case class Colour(red: Int, green: Int, blue: String) {
val other: Int = 42
}
val RBG = Colour(1,3,"isBlue")
I want to get a list (or array or any iterator for that matter) that would have the fields declared in the constructor as tuple values like these:
[(red, 1),(green, 3),(blue, "isBlue")]
I know the fact that there are a lot of examples on the net regarding the same issue but as I said, I wanted to know what should be the most ideal way to extract the required information
If you use Scala 2.10 reflection, this answer is half of the things you need. It will give you the method symbols of the case class, so you know the order and names of arguments:
import scala.reflect.runtime.{universe => ru}
import ru._
def getCaseMethods[T: TypeTag] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
case class Person(name: String, age: Int)
getCaseMethods[Person] // -> List(value age, value name)
You can call .name.toString on these methods to get the corresponding method names.
The next step is to invoke these methods on a given instance. You need a runtime mirror for that
val rm = runtimeMirror(getClass.getClassLoader)
Then you can "mirror" an actual instance:
val p = Person("foo", 33)
val pr = rm.reflect(p)
Then you can reflect on pr each method using reflectMethod and execute it via apply. Without going through each step separately, here is a solution altogether (see the val value = line for the mechanism of extracting a parameter's value):
def caseMap[T: TypeTag: reflect.ClassTag](instance: T): List[(String, Any)] = {
val im = rm.reflect(instance)
typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor =>
val name = m.name.toString
val value = im.reflectMethod(m).apply()
(name, value)
} (collection.breakOut)
}
caseMap(p) // -> List(age -> 33, name -> foo)
Every case object is a product, therefore you can use an iterator to get all its parameters' names and another iterator to get all its parameters' values:
case class Colour(red: Int, green: Int, blue: String) {
val other: Int = 42
}
val rgb = Colour(1, 3, "isBlue")
val names = rgb.productElementNames.toList // List(red, green, blue)
val values = rgb.productIterator.toList // List(1, 3, isBlue)
names.zip(values).foreach(print) // (red,1)(green,3)(blue,isBlue)
By product I mean both Cartesian product and an instance of Product. This requires Scala 2.13.0; although Product was available before, the iterator to get elements' names was only added in version 2.13.0.
Notice that no reflection is needed.
Assume that we have a class constructor that takes parameters that have default value.
class A(val p1 : Int = 3, val p2 : Int = 4)
Let's say I don't have control over this class and can't modify it in anyway. What I want to do is to call A's constructor with p1 = 5, p2 = (if condition1 == true then 5 else default value). One way to do this is
if(condition1)
x = new A(5,5)
else
x = new A(5)
As you can see, this can easily get big if there are many parameters and each must be supplied conditionally. What I want is something like
x = new A(p1 = 5, p2 = <if condition1 = true then 5 else default>)
How can I do that? Note that the fields in class A are vals, so I cant change them after instantiating A.
It seems to me you have three possibilities:
Create variables to hold each of the values you want to specify, do all the code to fill in the values, and instantiate A once at the end. This requires knowing the default values, as Ionut mentioned. I don't think creating a throwaway object to read the defaults is all that hackish -- certainly not as much as embedding the defaults themseves -- but whatever.
Use the reflection API to create A. I'm not exactly sure how to do that but almost certainly you can pass in a list of parameters, with any unspecified parameters defaulted. This requires Scala 2.10; before that, only the Java reflection API was available and you'd have to hack through the internal implementation of optional parameters, which is hackish.
Use macros. Also 2.10+. I think that quasiquotes should make it possible to do this without too much difficulty, although I'm not too familiar with them so I can't say for sure.
Fetch the default values,
val defaultA = new A()
Then
val x = new A(p1 = 5, p2 = if (cond) 5 else defaultA.p2)
Here's the quick and sort of wrong answer:
x = new A(p1 = 5, if (condition1) 5 else A.$lessinit$greater$default$2)
The "wrong" part is that this works in 2.10 but not in 2.9. The magick name for the default method has changed from version to version (notably, between 2.9 and 2.10), so it's safer to look up its name and call it via reflection. See Instantiating a case class with default args via reflection, How do I access default parameter values via Scala reflection? and Scala dynamic instantiation with default arguments.
What about having a derived class from A like this:
class D(val t2: (Boolean, Int)) extends A {
override val p2: Int = if(t2._1) t2._2 else A.init$default$2
}
Now you can instantiate A like this:
x = new D((condition1, 5))
If you have more parameters, then you can add a similar override statement and tuple parameter for each.
I can't help feeling what you are asking for is a bit unreasonable. As stated already it should be possible with macros. But generally if you are not satisfied with the provided constructor / factory, then you have to write you're own wrapper / factory. Personally I think that we may want to look a fresh at the whole issue of default values, Null objects and the Option class. However it is possible to cut out boilerplate, without macros. Put a an implicit Boolean in your utility package:
implicit class BooleanRich2(n : Boolean) {
def apply[T](v1: T, v2: T): T = if (n) v1 else v2
}
Then say we wish to use the following class that we can't modify:
final class A(val p1: Int = 1, val p2: Int = 2, val p3: Int = 3, val p4: Int = 4){
override def toString = s"p1: $p1, p2: $p2, p3: $p3, p4: $p4"
}
We can use it as follows:
var c1 = true
var c2 = false
def c3(s: String) = (s =="")
val a = new A(c1(20, 1)) //p1: 20, p2: 2, p3: 3, p4: 4
println(a) //p1: 20, p2: 2, p3: 3, p4: 4
val b = new A(p3 = c2(20, 3), p4 = c3("")(20, 4))
println(b) //p1: 1, p2: 2, p3: 3, p4: 20
val c = new A(p3 = c1(20, 3), p4 = c3("A non empty String")(20, 4))
println(c) //p1: 1, p2: 2, p3: 20, p4: 4
I have the following class representing some amount of money:
class Money(initDollars: Int, initCents: Int){
require (initDollars >= 0 && initCents >= 0)
private def this(positive: Boolean, initDollars: Int, initCents: Int) = {
this(initDollars, initCents)
//this.positive = positive
}
val positive: Boolean = true
val dollars = initDollars + initCents/100
val cents = initCents % 100
private val totalAmount = dollars * 100 + cents
def unary_- = new Money(!positive, dollars, cents)
}
object Money{
def apply(initDollars: Int, initCents: Int) = new Money(initDollars, initCents)
}
The amount can also be negative and I want to create it like this:
val am = -Money(1, 20)
So I want to initialize val positive from a secondary constructor, but I can't do it, because it's reassignment to val. I also can't add val in the list of parameters to secondary constructors. Could someone help?
Do it the other way around.
class Money private (pos: Boolean, initDollars: Int, initCents: Int) {
require (initDollars >= 0 && initCents >= 0)
def this(initDollars: Int, initCents: Int) = {
this(true, initDollars, initCents)
}
val positive: Boolean = pos
val dollars = initDollars + initCents/100
val cents = initCents % 100
private val totalAmount = dollars * 100 + cents
def unary_- = new Money(!positive, dollars, cents)
}
I think in order to achieve what you want, you have to do the following structural modification:
class Money(initDollars: Int, initCents: Int, positive: Boolean)
That way, you can then write a constructor that has less parameters without a problem (e.g. omitting "positive"). Doing it the other way around will be hard in Scala, if you want to stick to immutable values.
If you have the following declaration in your class body:
val positive: Boolean = true
You won't be able to change positive anymore, no way around it :)
Switch to one constructor, add positive to the end and give it a default value of true like this:
positive:Boolean = true
This way if defaults to true if not supplied. Then do the same for your apply function and invoke the 3 field constructor in apply.