- scala> val nodupes = Map(1 -> "a", 2-> "b", 3 -> "c")
- nodupes: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,a), (2,b), (3,c))
- // Scala 2.8+
- scala> nodupes map {_.swap}
- res4: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,2), (c,3))
- // Scala 2.7
- scala> Map() ++ (nodupes map {case (k,v) => (v,k)})
- res5: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,2), (c,3))
- // watch out if the values have duplicates you will loose information:
- scala> val dupes = Map(1 -> "a", 2-> "b", 3 -> "b")
- dupes: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,a), (2,b), (3,b))
- scala> dupes map {_.swap}
- res6: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,3))
- // a way to not loose any data
- scala> dupes groupBy {_._2} map {case (key,value) => (key, value.unzip._1)}
- res12: scala.collection.Map[java.lang.String,scala.collection.immutable.Iterable[Int]] = Map((a,List(1)), (b,List(2, 3)))
- // I wanted to do the following for performance:
- scala> dupes.view groupBy {_._2} map {case (key,value) => (key, value.unzip._1)}
- java.lang.UnsupportedOperationException: IterableView((1,a), (2,b), (3,b)).newBuilder
- at scala.collection.TraversableViewLike$class.newBuilder(TraversableViewLike.scala:40)
- at scala.collection.IterableLike$$anon$1.newBuilder(IterableLike.scala:363)
- at scala.collection.TraversableLike$$anonfun$groupBy$1.apply(TraversableLike.scala:370)
- // but as you can see a view cannot yet be grouped. Perhaps in the future.
Wednesday, March 10, 2010
How to reverse a map
Suppose you wish to take a map and swap the keys with values. The stackoverflow question Elegant way to revers a map in scala offers some good suggestions
Labels:
groupby,
intermediate,
Map,
Scala,
swap
Monday, March 8, 2010
Lazy man's random test data
A quick tip for generating some random testdata.
Note: This is a poor man's solution to using ScalaCheck. If you can handle the dependency I would really recommend using that library.
Note: This is a poor man's solution to using ScalaCheck. If you can handle the dependency I would really recommend using that library.
- scala> object Options extends Enumeration {
- | val ONE, TWO, THREE, FOUR = Value
- | }
- defined module Options
- /*
- Randomly select zero or more elements from the options enumeration
- */
- scala> Options.values.filter {_ => util.Random.nextBoolean} mkString ", "
- res2: String = TWO, FOUR
- /*
- Select a random string.
- Warning: there is no restriction on the characters so control characters are likely
- */
- scala> util.Random.nextString(10)
- res5: String = ??????????
- /*
- ASCII string is oftern more useful for test data. This selects a random string up to 13 characters long
- */
- scala> util.Random.nextASCIIString(13)
- res6: java.lang.String = RVPD\#_HqJ8:o
- /*
- This creates a sequence of 10 random strings
- */
- scala> 1 to 10 map {_ => util.Random.nextASCIIString(13)}
- res7: scala.collection.immutable.IndexedSeq[java.lang.String] = IndexedSeq(;E8|Q8H8RI;Q=, vM-X;"ksBr\:c, SKyz{uXNQ5E]X, =Jd8_ll08)s%e, gRCs)6wj%C-YF, `x;2Zru?l*c%@, XE*/Rx9:qPfpm, s|u,e.un+-Xm(, M,TpX9Dq-6$+^, w;exER�|}Ya)
Saturday, March 6, 2010
Blocks within if statements
his is another topic that examines the consistency of Scala. This topic examines blocks in if statements. It is related to Blocks within for comprehensions and Temporary Variables during object creation.
- // standard if
- if(1 > 2) -1 else 0
- // since blocks return a value you can use a block within the if statement
- // (not sure when you would want to but...)
- if ({ val x = 1
- val y = 2
- x == y }) 1 else 2
Thursday, March 4, 2010
Zip with a constant value
A simple tip for zipping a List (or other collection) with a single value.
- scala> Stream.continually("h") zip List(1,2,3,4)
- res2: scala.collection.immutable.Stream[(java.lang.String, Int)] = Stream((h,1), ?)
- scala> res2 mkString ","
- res3: String = (h,1),(h,2),(h,3),(h,4)
- scala> List(1,2,3,4) zip Stream.continually("h")
- res4: List[(Int, java.lang.String)] = List((1,h), (2,h), (3,h), (4,h))
Labels:
collections,
Scala,
stream,
zip
Wednesday, March 3, 2010
Functions using case statements
A further tip regarding using case statements to construct functions. If a case statement is assigned to a Function it will construct a Function object not a PartialFunction.
I suppose the question is why do you care about this since PartialFunction is a Function. The fact is that a PartialFunction is a Function1. But using a case statement you can construct a Function4 very easily.
I suppose the question is why do you care about this since PartialFunction is a Function. The fact is that a PartialFunction is a Function1. But using a case statement you can construct a Function4 very easily.
- scala> def run(f : Function1[Any,Unit]) = println(f.isInstanceOf[PartialFunction[_,_]])
- run: (f: (Any) => Unit)Unit
- /*
- since run expects a Function calling run as shown here will make a
- Function object not a PartialFunction Object
- */
- scala> run({case f => ()})
- false
- scala> def pf(f : PartialFunction[Any,Unit]) = println(f.isInstanceOf[PartialFunction[_,_]])
- pf: (f: PartialFunction[Any,Unit])Unit
- // Now a PartialFunction will be created
- scala> pf({case f => ()})
- true
- scala> def run(f : Function2[Int,String,Unit]) = f(1,"2")
- run: (f: (Int, String) => Unit)Unit
- /*
- This demonstrates why it is important that a case creates a Function
- when assigned to a Function. PartialFunctions are Function1 objects
- but the following statement is creating a Function2 object.
- */
- scala> run({
- | case (1,b) => println(b)
- | case (a,b) => println(a,b)
- | })
- 2
Labels:
case,
function,
intermediate,
partial-function,
Scala
Tuesday, March 2, 2010
Methods on PartialFunction
This topic inspects the methods defined in the PartialFunction Object.
- scala> type PF = PartialFunction[Int,Int]
- defined type alias PF
- // the two partial functions that we will use for the examples
- scala> val pf1 : PF = {case 1 => 2}
- pf1: PF = < function1>
- scala> val pf2 : PF = {case 2 => 3}
- pf2: PF = < function1>
- /*
- As is well known, when a PartialFunction is called with a value
- it must be defined at that value or bad things will happen
- */
- scala> pf1 isDefinedAt 1
- res14: Boolean = true
- scala> pf1 isDefinedAt 2
- res15: Boolean = false
- scala> pf1(2)
- scala.MatchError: 2
- at $anonfun$1.apply(< console>:5)
- at $anonfun$1.apply(< console>:5)
- at .< init>(< console>:7)
- ...
- scala> pf1(1)
- res5: Int = 2
- /*
- It is possible to compose two partial functions so first one partialFunction is called and then the next
- */
- scala> (pf1 andThen pf2) isDefinedAt 2
- res16: Boolean = false
- scala> (pf1 andThen pf2) isDefinedAt 1
- res17: Boolean = true
- scala> (pf1 andThen pf2)(2)
- scala.MatchError: 2
- at $anonfun$1.apply(< console>:5)
- at $anonfun$1.apply(< console>:5)
- at scala.PartialFunction$$anon$2.apply(PartialFunction.scala:59)
- at .< init>(< console>:8)
- ...
- scala> (pf1 andThen pf2)(1)
- res8: Int = 3
- /*
- An alternative way of combining PartialFunctions is to 'or' them
- */
- scala> (pf1 orElse pf2) isDefinedAt 1
- res18: Boolean = true
- scala> (pf1 orElse pf2) isDefinedAt 2
- res19: Boolean = true
- scala> (pf1 orElse pf2) isDefinedAt 3
- res20: Boolean = false
- scala> (pf1 orElse pf2)(1)
- res9: Int = 2
- scala> (pf1 orElse pf2)(2)
- res10: Int = 3
- /*
- Finally a PartialFunction can be easily converted to a function that returns
- an Option
- */
- scala> pf1.lift
- res21: (Int) => Option[Int] = < function1>
- scala> pf1.lift(1)
- res11: Option[Int] = Some(2)
- scala> pf1.lift(2)
- res12: Option[Int] = None
Labels:
case,
intermediate,
partial-function,
Scala
Monday, March 1, 2010
NullPointer when mixed traits (Warning)
This tip is mainly to document a 'GOTCHA' that I got caught by recently. It basically goes like this:
Trait Y extends(or has self-type) X. Trait X defines some abstract method 'm'. The initialization code in Y accesses 'm'. Creation of an object new X with Y results in: *Boom* NullPointerException (on object creation).
The example in code:
At a glance it seems that x should override the abstract value x in trait X. However the order in which traits are declared is important. In this case first Y is configured then X. Since X is not yet configured Y throws an exception. There are several ways to work around this.
Option 1:
Option 2:
Option 3:
Option 4:
Two more warnings. First, the same error will occur whether 'x' is a def or a val or a var.
Second warning: In complex domain models it is easy to have a case where Y extends X but the final object is created as: new X with Y{...}.
You will get the same error here because (I think) the compiler recognized that Y is being mixed in with X and therefore the X will be initialized as after Y instead of before Y.
First the code:
If the code instantiated new Y{...} the initialization would be X then Y. Because X can only be initialized once, the explicit declaration of new X with Y forces Y to be initialized before X. (X can only be initialized once even when it appears twice in the hierarchy).
This is a topic called linearization and will be addressed in the future.
Trait Y extends(or has self-type) X. Trait X defines some abstract method 'm'. The initialization code in Y accesses 'm'. Creation of an object new X with Y results in: *Boom* NullPointerException (on object creation).
The example in code:
- scala> trait X { val x : java.io.File }
- defined trait X
- scala> trait Y {self : X => ; val y = x.getName}
- defined trait Y
- scala> new X with Y { val x = new java.io.File("hi")}
- java.lang.NullPointerException
- at Y$class.$init$(< console>:5)
- at $anon$1.< init>(< console>:7)
- ...
At a glance it seems that x should override the abstract value x in trait X. However the order in which traits are declared is important. In this case first Y is configured then X. Since X is not yet configured Y throws an exception. There are several ways to work around this.
Option 1:
- trait X {val x : java.io.File}
- trait Y {self : X => ; val y = x.getName}
- /*
- Declaring Y with X will work because Y is initialized after X
- but remember that there may
- be other reasons that X with Y is required.
- Method resolution is one such reason
- */
- new Y with X { val x = new java.io.File("hi")}
Option 2:
- trait X { val x : java.io.File }
- trait Y {self : X => ; def y = x.getName}
- /*
- Since method y is a 'def' x.getName will not be executed during initialization.
- */
- scala> new X with Y { val x = new java.io.File("hi")}
- res10: java.lang.Object with X with Y = $anon$1@7cb9e9a3
Option 3:
- trait X { val x : java.io.File }
- trait Y {self : X => ; lazy val y = x.getName}
- /*
- 'lazy val' works for the same reason 'def' works: x.getName is not invoked during initialization
- */
- scala> new X with Y { val x = new java.io.File("hi")}
- res10: java.lang.Object with X with Y = $anon$1@7cb9e9a3
Option 4:
- trait X {val x : java.io.File }
- trait Y extends X {def y = x.getName}
- /*
- if Y extends X then a new Y can be instantiated
- */
- new Y {val x = new java.io.File("hi")}
Two more warnings. First, the same error will occur whether 'x' is a def or a val or a var.
- trait X { def x : java.io.File }
- trait Y {self : X => ; val y = x.getName}
- new X with Y { val x = new java.io.File("hi")}
Second warning: In complex domain models it is easy to have a case where Y extends X but the final object is created as: new X with Y{...}.
You will get the same error here because (I think) the compiler recognized that Y is being mixed in with X and therefore the X will be initialized as after Y instead of before Y.
First the code:
- trait X { def x : java.io.File }
- trait Y extends X { val y = x.getName}
- new X with Y { val x = new java.io.File("hi")}
If the code instantiated new Y{...} the initialization would be X then Y. Because X can only be initialized once, the explicit declaration of new X with Y forces Y to be initialized before X. (X can only be initialized once even when it appears twice in the hierarchy).
This is a topic called linearization and will be addressed in the future.
Labels:
inheritance,
intermediate,
linearization,
Scala,
traits,
with
Subscribe to:
Posts (Atom)