Initialize a val from a secondary constructor - scala

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.

Related

Scala: How to define an enum with extra attributes?

I have a use-case where I need to define a new enum type LongShort but I need it in a way to also carry the sign so it can be directly used in mathematical expressions e.g.
object LongShortType extends Enumeration {
type Type = Value
val Long = Value(+1)
val Short = Value(-1)
}
I'd then like to use it like this:
val longShort = LongShortType.Short
val numberOfContracts: Int = 10
val vanillaOptionNotional: Double = longShort*numberOfContracts
but this leads to compiler error cannot resolve symbol * ... is there a way to extract the value of the enum? Or am I not understanding how enum types work?
The type of LongShortType.Short isn't Int, it's Value. You can either extract the underlying id of the value:
val longShort = LongShortType.Short.id
Which is a little ugly. Or you could not use an enum type at all:
object LongShortType {
val Long = 1
val Short = -1
}
And then your equation would work as is.
OK I worked out a solution to accomplish what I wanted without any compromisse and by that I mean that this solution has all the advantages of using Scala enum e.g. the withName and still allows me to define extra features on it:
object LongShortType extends Enumeration {
type Type = LongShortVal
val Long = Value("Long", +1)
val Short = Value("Short", -1)
case class LongShortVal(name: String, sign: Int) extends Val(nextId, name)
protected final def Value(name: String, sign: Int) = new LongShortVal(name, sign)
}
and now can do:
val longShort = LongShortType.Short
val numberOfContracts: Int = 10
val vanillaOptionNotional: Double = longShort.sign*numberOfContracts
and can also do:
val longShort = LongShort.withName("Long") // returns LongShort.Long

Sort scala arrayBuffer of TimeStamp

I have this function:
def getTime() : ArrayBuffer[Timestamp] = {
val offset = Timestamp.valueOf("2015-01-01 00:00:00").getTime()
val end = Timestamp.valueOf("2015-01-02 00:00:00").getTime()
val diff = end - offset + 1
val mList = ArrayBuffer[Timestamp]()
val numRecords = 3
var i = 0
while (i < numRecords) {
val rand = new Timestamp(offset + (Math.random() * diff).toLong)
mList += rand
i += 1
}
// mList.toList.sortWith(_ < _);
// scala.util.Sorting.quickSort(mList.toArray);
}
I have tried to sort the array but could not. I get this error:
No implicit Ordering defined for java.sql.Timestamp.
I know I need to define how the ordering would be done. Is there a way to sort it easily as in Java: Collections.sort(list);
or there is a better approach using Scala?
Alternatively, define it somewhere in your class and you're good to go:
implicit def ordered: Ordering[Timestamp] = new Ordering[Timestamp] {
def compare(x: Timestamp, y: Timestamp): Int = x compareTo y
}
getTime().sorted // now this will work just fine
mList.sortWith(_.compareTo(_) < 1)
Note that's with an anonymous function, you could pass an explicit function, which would look like this:
def comparator(first: Timestamp, second: Timestamp) = first.compareTo(second) < 1
mList.sortWith(comparator)
There's no implicit ordering on Timestamp itself, here we're just sorting using the compareTo method.
Thanks to #Nick for pointing out sorting on getTime() wasn't suffient in all scenarios. I also looked at the before method which you would expect to work, but this only compares using the epoch value as well.

scala var args caseclasses

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")
}

Scala immutable container class extended with mixins

I'd like a container class that I can extend with some number of traits to contain groups of default vals that can later be changed in an immutable way. The traits will hold certain simple pieces of data that go together so that creating the class with a couple of traits will create an object with several collections of default values.
Then I'd like to be able to modify any of the vals immutably by copying the object while changing one new value at a time.
The class might have something like the following:
class Defaults(val string: String = "string", val int: Int = "int")
Then other traits like this
trait MoreDefaults{
val long: Long = 1l
}
Then I'd like to mix them when instantiated to build my the particular needed set of defaults
var d = new Defaults with MoreDefaults
and later to something like:
if (someFlag) d = d.copy( long = 1412341234l )
You can do something like this with a single case class but I run out of params at 22. But I'll have a bunch of groupings of defaults I'd like to mixin depending on the need, then allow changes to any of them (class defined or trait defined) in an immutable way.
I can stick a copy method in the Defaults class like this:
def copy(
string: String = string,
int: Int = int): Defaults = {
new Defaults(string, int)
}
then do something like
var d = new Defaults
if (someFlag) d = d.copy(int = 234234)
Question ====> This works for values in the base class but I can't figure how to extend this to the mixin traits. Ideally the d.copy would work on all vals defined by all of the class + traits. Overloading is trouble too since the vals are mainly Strings but all of the val names will be unique in any mix of class and traits or it is an error.
Using only classes I can get some of this functionality by having a base Defaults class then extending it with another class that has it's own non-overloaded copyMoreDefault function. This is really ugly and I hope a Scala expert will see it and have a good laugh before setting me straight--it does work though.
class Defaults(
val string: String = "one",
val boolean: Boolean = true,
val int: Int = 1,
val double: Double = 1.0d,
val long: Long = 1l) {
def copy(
string: String = string,
boolean: Boolean = boolean,
int: Int = int,
double: Double = double,
long: Long = long): Defaults = {
new Defaults(string, boolean, int, double, long)
}
}
class MoreDefaults(
string: String = "one",
boolean: Boolean = true,
int: Int = 1,
double: Double = 1.0d,
long: Long = 1l,
val string2: String = "string2") extends Defaults (
string,
boolean,
int,
double,
long) {
def copyMoreDefaults(
string: String = string,
boolean: Boolean = boolean,
int: Int = int,
double: Double = double,
long: Long = long,
string2: String = string2): MoreDefaults = {
new MoreDefaults(string, boolean, int, double, long, string2)
}
}
Then the following works:
var d = new MoreDefualts
if (someFlag) d = d.copyMoreDefaults(string2 = "new string2")
This method will be a mess if Defaults get's changed parameters! All the derived classes will have to be updated--ugh. There must be a better way.
I don't think I'm strictly speaking answering your question, rather suggesting an alternative solution. So your having problems with large case classes, e.g.
case class Fred(a: Int = 1, b: Int = 2, ... too many params ... )
What I would do is organize the params into more case classes:
case class Bar(a: Int = 1, b: Int = 2)
case class Foo(c: Int = 99, d: Int = 200)
// etc
case class Fred(bar: Bar = Bar(), foo: Foo = Foo(), ... etc)
Then when you want to do a copy and change, say one of the values of Foo you do:
val myFred: Fred = Fred()
val fredCopy: Fred = myFred.copy(foo = myFred.foo.copy(d = 300))
and you need not even define the copy functions, you get them for free.

Incrementing 'i' in scala for loop by differing amounts depending on circumstance

I want to write a for loop in scala, but the counter should get incremented by more than one (the amount is variable) in some special cases.
You can do this with a combination of a filter and an external var. Here is an example:
var nextValidVal = 0
for (i <- 0 to 99; if i >= nextValidVal) {
var amountToSkip = 0
// Whatever this loop is for
nextValidVal = if (amountToSkip > 0) i + amountToSkip + 1 else nextValidVal
}
So in the main body of your loop, you can set amountToSkip to n according to your conditions. The next n values of i´s sequence will be skipped.
If your sequence is pulled from some other kind of sequence, you could do it like this
var skip = 0
for (o <- someCollection if { val res = skip == 0; skip = if (!res) skip - 1 else 0; res } ) {
// Do stuff
}
If you set skip to a positive value in the body of the loop, the next n elements of the sequence will be skipped.
Of course, this is terribly imperative and side-effecty. I would look for other ways to to this where ever possible, by mapping or filtering or folding the original sequence.
You could implement your own stream to reflect step, for example:
import scala.collection.immutable.Stream
import ForStream._
object Test {
def main(args: Array[String]): Unit = {
val range = 0 to 20 by 1 withVariableStep; // in case you like definition through range
//val range = ForStream(0,20,1) // direct definition
for (i<- range) {
println(s"i=$i")
range.step = range.step + 1
}
}
}
object ForStream{
implicit def toForStream(range: Range): ForStream = new ForStreamMaster(range.start, range.end,range.step)
def apply(head:Int, end:Int, step:Int) = new ForStreamMaster(head, end,step)
}
abstract class ForStream(override val head: Int, val end: Int, var step: Int) extends Stream[Int] {
override val tailDefined = false
override val isEmpty = head > end
def withVariableStep = this
}
class ForStreamMaster(_head: Int, _end: Int, _Step: Int) extends ForStream(_head, _end,_Step){
override def tail = if (isEmpty) Stream.Empty else new ForStreamSlave(head + step, end, step, this)
}
class ForStreamSlave(_head: Int, _end: Int, _step: Int, val master: ForStream) extends ForStream(_head, _end,_step){
override def tail = if (isEmpty) Stream.Empty else new ForStreamSlave(head + master.step, end, master.step, master)
}
This prints:
i=0
i=2
i=5
i=9
i=14
i=20
You can define ForStream from Range with implicits, or define it directly. But be carefull:
You are not iterating Range anymore!
Stream should be immutable, but step is mutable!
Also as #om-nom-nom noted, this might be better implemented with recursion
Why not use the do-while loop?
var x = 0;
do{
...something
if(condition){change x to something else}
else{something else}
x+=1
}while(some condition for x)