Trying to optimize next piece of code:
private val players = scala.collection.mutable.ListBuffer.empty[Player]
private def MaxDice(p1: Player, p2: Player): Player = if (p1.LastDice > p2.LastDice) p1 else p2
private def MinDice(p1: Player, p2: Player): Player = if (p1.LastDice < p2.LastDice) p1 else p2
def NextPlayer() : Option[Player] = {
val maxPlayer = this.players.reduceLeft(MaxDice)
val minPlayer = this.players.reduceLeft(MinDice)
if (maxPlayer.LastDice == minPlayer.LastDice)
None
else
Option(maxPlayer)
}
is there a way to do this in more sophisticated way?
If you need to do comparison is other places of the app, you can extract the Ordering into Player's companion object:
Welcome to Scala version 2.11.7 (OpenJDK 64-Bit Server VM, Java 1.8.0_66-internal).
Type in expressions to have them evaluated.
Type :help for more information.
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Player(lastDice: Int)
object Player {
implicit val ordering: Ordering[Player] = Ordering.by(_.lastDice)
}
val players = List(Player(2), Player(2))
players.max == players.min
// Exiting paste mode, now interpreting.
defined class Player
defined object Player
players: List[Player] = List(Player(2), Player(2))
res0: Boolean = true
Addendum:
Another thing to consider is the size of the list. max and min will throw exception if list is empty:
def min[B >: A](implicit cmp: Ordering[B]): A = {
if (isEmpty)
throw new UnsupportedOperationException("empty.min")
reduceLeft((x, y) => if (cmp.lteq(x, y)) x else y)
}
def max[B >: A](implicit cmp: Ordering[B]): A = {
if (isEmpty)
throw new UnsupportedOperationException("empty.max")
reduceLeft((x, y) => if (cmp.gteq(x, y)) x else y)
}
And if list only contains 1 player, your logic will incorrectly give you None. To take all this into account the easiest thing might be working with the sorted list.
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Player(lastDice: Int)
object Player {
implicit val orderingDescending: Ordering[Player] =
Ordering.fromLessThan(_.lastDice > _.lastDice)
def nextPlayer(players: List[Player]): Option[Player] = {
val sorted = players.sorted
sorted match {
case first :: Nil => Some(first)
case first :: second :: _ if first.lastDice != second.lastDice => Some(first)
case _ => None
}
}
}
// Exiting paste mode, now interpreting.
defined class Player
defined object Player
scala> Player.nextPlayer(List[Player]())
res2: Option[Player] = None
scala> Player.nextPlayer(List(Player(1)))
res3: Option[Player] = Some(Player(1))
scala> Player.nextPlayer(List(Player(1), Player(4)))
res5: Option[Player] = Some(Player(4))
scala> Player.nextPlayer(List(Player(1), Player(4), Player(4)))
res6: Option[Player] = None
scala> Player.nextPlayer(List(Player(1), Player(1), Player(4)))
res7: Option[Player] = Some(Player(4))
NB: You'll have to change ordering to descending.
You can use the maxBy and minBy methods:
val maxPlayer = this.players.maxBy(_.LastDice)
val minPlayer = this.players.minBy(_.LastDice)
Otherwise, there's not much to optimize I think.
Assume I have an instance of MethodMirror created for a certain method of an object. By mirror's fields I can easily access return type and parameters of the method. But I actually need to obtain the type this method would have as a function.
Here is a toy code example which will help me explain, what I want to achieve. I'm using Scala 2.11.6.
import scala.reflect.runtime.universe._
object ForStackOverflow {
object Obj {
def method(x:String, y:String):Int = 0
def expectedRetType():((String, String) => Int) = ???
}
def main(args: Array[String]) {
val mirror:Mirror = runtimeMirror(getClass.getClassLoader)
val instanceMirror = mirror.reflect(Obj)
val methodSymbol:MethodSymbol = instanceMirror.symbol.toType.decl(TermName("method")).asMethod
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
println(methodMirror.symbol.returnType)
println(methodMirror.symbol.paramLists(0).map { x => x.info.resultType }.mkString(", "))
val expectedSymbol:MethodSymbol = instanceMirror.symbol.toType.decl(TermName("expectedRetType")).asMethod
println("I would like to produce from a 'methodMirror' this: "+expectedSymbol.returnType)
}
}
I want to produce Type instance from the methodMirror which would represent a function. For this example it should be (String, String) => Int. I would prefer a solution that doesn't depend too much on the concrete Scala's FunctionX classes.
The method getEtaExpandedMethodType below does what you asked, and even handles methods with multiple parameter lists.
On the other hand it does not handle generic methods. By example def method[T](x: T) = 123, when eta-expanded, creates a function of type Any => Int, but getEtaExpandedMethodType will report T => Int which is not only incorrect but does not make sense at all (T has no meaning in this context).
def getEtaExpandedMethodType(methodSymbol: MethodSymbol): Type = {
val typ = methodSymbol.typeSignature
def paramType(paramSymbol: Symbol): Type = {
// TODO: handle the case where paramSymbol denotes a type parameter
paramSymbol.typeSignatureIn(typ)
}
def rec(paramLists: List[List[Symbol]]): Type = {
paramLists match {
case Nil => methodSymbol.returnType
case params :: otherParams =>
val functionClassSymbol = definitions.FunctionClass(params.length)
appliedType(functionClassSymbol, params.map(paramType) :+ rec(otherParams))
}
}
if (methodSymbol.paramLists.isEmpty) { // No arg method
appliedType(definitions.FunctionClass(0), List(methodSymbol.returnType))
} else {
rec(methodSymbol.paramLists)
}
}
def getEtaExpandedMethodType(methodMirror: MethodMirror): Type = getEtaExpandedMethodType(methodMirror.symbol)
REPL test:
scala> val mirror: Mirror = runtimeMirror(getClass.getClassLoader)
mirror: reflect.runtime.universe.Mirror = ...
scala> val instanceMirror = mirror.reflect(Obj)
instanceMirror: reflect.runtime.universe.InstanceMirror = instance mirror for Obj$#21b6e507
scala> val tpe = instanceMirror.symbol.toType
tpe: reflect.runtime.universe.Type = Obj.type
scala> getEtaExpandedMethodType(tpe.decl(TermName("method1")).asMethod)
res28: reflect.runtime.universe.Type = (String, String) => scala.Int
scala> getEtaExpandedMethodType(tpe.decl(TermName("method2")).asMethod)
res29: reflect.runtime.universe.Type = () => String
scala> getEtaExpandedMethodType(tpe.decl(TermName("method3")).asMethod)
res30: reflect.runtime.universe.Type = () => scala.Long
scala> getEtaExpandedMethodType(tpe.decl(TermName("method4")).asMethod)
res31: reflect.runtime.universe.Type = String => (scala.Float => scala.Double)
scala> getEtaExpandedMethodType(tpe.decl(TermName("method5")).asMethod)
res32: reflect.runtime.universe.Type = T => scala.Int
scala> getEtaExpandedMethodType(tpe.decl(TermName("method6")).asMethod)
res33: reflect.runtime.universe.Type = T => scala.Int
Here is probably the most straightforward solution using universe.appliedType. It doesn't work in the case of multiple parameter lists. I post this to show an alternative way of solving this problem.
def getEtaExpandedMethodType2(methodSymbol: MethodSymbol): Type = {
val typesList = methodSymbol.info.paramLists(0).map(x => x.typeSignature) :+ methodSymbol.returnType
val arity = methodSymbol.paramLists(0).size
universe.appliedType(definitions.FunctionClass(arity), typesList)
}
I'm using Scala 2.10.1 and I'm trying the define a method which will retrieve all the vals (including the inherited ones) from an object.
I have the following:
import scala.reflect.runtime.{universe => ru}
object Reflection {
val mirror = ru.runtimeMirror(this.getClass.getClassLoader)
def findVals(x: Any): Iterable[String] = {
val theType = mirror.classSymbol(x.getClass).toType
theType.members.collect({case x if x.isTerm => x.asTerm}).filter(_.isVal).map(_.name.toString)
}
}
I am testing on these two classes:
class Base {
val x = 10
}
class Child extends Base {
val y = 20
}
When calling the following code:
val x = new Child
val vs = Reflection.findVals(x)
println(vs)
The result is List(y)
For some reason, the isVal method returns false for the term corresponding to the x field from the Base class.
Can someone tell me what's the problem here? Am I doing something wrong?
Per Why don't Scala case class fields reflect as public? you should use isAccessor instead of isVal.
I'm actually using isGetter and setter to properly filter vars per your comment:
def findVals(x: Any): Iterable[String] = {
val theType = mirror.classSymbol(x.getClass).toType
val xtm = theType.members.collect({case x if x.isTerm => x.asTerm})
xtm.filter(m => m.isGetter && !xtm.exists(m.setter == _)).map(_.name.toString)
}
Results:
scala> class Base {
| var x = 10
| val xx = 2
| }
defined class Base
scala> class Child extends Base {
| val y = 3
| }
defined class Child
scala> val x = new Child
x: Child = Child#1c0026e
scala> val vs = Reflection.findVals(x)
vs: Iterable[String] = List(y, xx)
scala> println(vs)
List(y, xx)
Using SMirror:
scala> implicit val mirror = scala.reflect.runtime.currentMirror
mirror: reflect.runtime.universe.Mirror = JavaMirror with scala.tool…
scala> import net.fwbrasil.smirror._
import net.fwbrasil.smirror._
scala> class Base {
val x = 10
}
defined class Base
scala> class Child extends Base {
val y = 20
}
defined class Child
scala> val x = new Child
x: Child = Child#448593d0
scala> x.reflect.vals
res5: List[net.fwbrasil.smirror.SInstanceVal[Child]] = List(val x: scala.Int (bound to Child#448593d0), val y: scala.Int (bound to Child#448593d0))
scala> x.reflect.vals.head.get
res7: Any = 10
So, this is terribly inelegant, but it seems to work:
import scala.reflect.runtime.{universe => ru}
object Reflection {
val mirror = ru.runtimeMirror(this.getClass.getClassLoader)
val ObjectClass = classOf[java.lang.Object];
def findVals(x: Any) : Iterable[String] = findVals( x.getClass, List.empty );
def findVals(clz: Class[_], accum : Iterable[String]): Iterable[String] = {
clz match {
case ObjectClass => accum;
case _ => {
val theType = mirror.classSymbol(clz).toType
val newVals = theType.members.collect({case x if x.isTerm => x.asTerm}).filter(_.isVal).map(_.name.toString)
findVals( clz.getSuperclass, accum ++ newVals )
}
}
}
}
Then...
scala> class Base {
| val x = 10
| var z = 20
| }
defined class Base
scala> class Child extends Base {
| val y = 20
| var a = 9
| }
defined class Child
scala> val x = new Child
x: Child = Child#3093266d
scala> val vs = Reflection.findVals(x)
vs: Iterable[String] = List("y ", "x ")
scala> println(vs)
List(y , x )
It seems that, at least for now, Scala reflection looks at the Java field to determine the presence of a val, so I guess you just have to climb the class hierarchy... I'm guessing it looks for the presence of a setter to distinguish val from var. Again, not so lovely, but functional.
I'm looking to create a class that is basically a collection with an extra field. However, I keep running into problems and am wondering what the best way of implementing this is. I've tried to follow the pattern given in the Scala book. E.g.
import scala.collection.IndexedSeqLike
import scala.collection.mutable.Builder
import scala.collection.generic.CanBuildFrom
import scala.collection.mutable.ArrayBuffer
class FieldSequence[FT,ST](val field: FT, seq: IndexedSeq[ST] = Vector())
extends IndexedSeq[ST] with IndexedSeqLike[ST,FieldSequence[FT,ST]] {
def apply(index: Int): ST = return seq(index)
def length = seq.length
override def newBuilder: Builder[ST,FieldSequence[FT,ST]]
= FieldSequence.newBuilder[FT,ST](field)
}
object FieldSequence {
def fromSeq[FT,ST](field: FT)(buf: IndexedSeq[ST])
= new FieldSequence(field, buf)
def newBuilder[FT,ST](field: FT): Builder[ST,FieldSequence[FT,ST]]
= new ArrayBuffer mapResult(fromSeq(field))
implicit def canBuildFrom[FT,ST]:
CanBuildFrom[FieldSequence[FT,ST], ST, FieldSequence[FT,ST]] =
new CanBuildFrom[FieldSequence[FT,ST], ST, FieldSequence[FT,ST]] {
def apply(): Builder[ST,FieldSequence[FT,ST]]
= newBuilder[FT,ST]( _ ) // What goes here?
def apply(from: FieldSequence[FT,ST]): Builder[ST,FieldSequence[FT,ST]]
= from.newBuilder
}
}
The problem is the CanBuildFrom that is implicitly defined needs an apply method with no arguments. But in these circumstances this method is meaningless, as a field (of type FT) is needed to construct a FieldSequence. In fact, it should be impossible to construct a FieldSequence, simply from a sequence of type ST. Is the best I can do to throw an exception here?
Then your class doesn't fulfill the requirements to be a Seq, and methods like flatMap (and hence for-comprehensions) can't work for it.
I'm not sure I agree with Landei about flatMap and map. If you replace with throwing an exception like this, most of the operations should work.
def apply(): Builder[ST,FieldSequence[FT,ST]] = sys.error("unsupported")
From what I can see in TraversableLike, map and flatMap and most other ones use the apply(repr) version. So for comprehensions seemingly work. It also feels like it should follow the Monad laws (the field is just carried accross).
Given the code you have, you can do this:
scala> val fs = FieldSequence.fromSeq("str")(Vector(1,2))
fs: FieldSequence[java.lang.String,Int] = FieldSequence(1, 2)
scala> fs.map(1 + _)
res3: FieldSequence[java.lang.String,Int] = FieldSequence(2, 3)
scala> val fs2 = FieldSequence.fromSeq("str1")(Vector(10,20))
fs2: FieldSequence[java.lang.String,Int] = FieldSequence(10, 20)
scala> for (x <- fs if x > 0; y <- fs2) yield (x + y)
res5: FieldSequence[java.lang.String,Int] = FieldSequence(11, 21, 12, 22)
What doesn't work is the following:
scala> fs.map(_ + "!")
// does not return a FieldSequence
scala> List(1,2).map(1 + _)(collection.breakOut): FieldSequence[String, Int]
java.lang.RuntimeException: unsupported
// this is where the apply() is used
For breakOut to work you would need to implement the apply() method. I suspect you could generate a builder with some default value for field: def apply() = newBuilder[FT, ST](getDefault) with some implementation of getDefault that makes sense for your use case.
For the fact that fs.map(_ + "!") does not preserve the type, you need to modify your signature and implementation, so that the compiler can find a CanBuildFrom[FieldSequence[String, Int], String, FieldSequence[String, String]]
implicit def canBuildFrom[FT,ST_FROM,ST]:
CanBuildFrom[FieldSequence[FT,ST_FROM], ST, FieldSequence[FT,ST]] =
new CanBuildFrom[FieldSequence[FT,ST_FROM], ST, FieldSequence[FT,ST]] {
def apply(): Builder[ST,FieldSequence[FT,ST]]
= sys.error("unsupported")
def apply(from: FieldSequence[FT,ST_FROM]): Builder[ST,FieldSequence[FT,ST]]
= newBuilder[FT, ST](from.field)
}
In the end, my answer was very similar to that in a previous question. The difference with that question and my original and the answer are slight but basically allow anything that has a sequence to be a sequence.
import scala.collection.SeqLike
import scala.collection.mutable.Builder
import scala.collection.mutable.ArrayBuffer
import scala.collection.generic.CanBuildFrom
trait SeqAdapter[+A, Repr[+X] <: SeqAdapter[X,Repr]]
extends Seq[A] with SeqLike[A,Repr[A]] {
val underlyingSeq: Seq[A]
def create[B](seq: Seq[B]): Repr[B]
def apply(index: Int) = underlyingSeq(index)
def length = underlyingSeq.length
def iterator = underlyingSeq.iterator
override protected[this] def newBuilder: Builder[A,Repr[A]] = {
val sac = new SeqAdapterCompanion[Repr] {
def createDefault[B](seq: Seq[B]) = create(seq)
}
sac.newBuilder(create)
}
}
trait SeqAdapterCompanion[Repr[+X] <: SeqAdapter[X,Repr]] {
def createDefault[A](seq: Seq[A]): Repr[A]
def fromSeq[A](creator: (Seq[A]) => Repr[A])(seq: Seq[A]) = creator(seq)
def newBuilder[A](creator: (Seq[A]) => Repr[A]): Builder[A,Repr[A]] =
new ArrayBuffer mapResult fromSeq(creator)
implicit def canBuildFrom[A,B]: CanBuildFrom[Repr[A],B,Repr[B]] =
new CanBuildFrom[Repr[A],B,Repr[B]] {
def apply(): Builder[B,Repr[B]] = newBuilder(createDefault)
def apply(from: Repr[A]) = newBuilder(from.create)
}
}
This fixes all the problems huynhjl brought up. For my original problem, to have a field and a sequence treated as a sequence, a simple class will now do.
trait Field[FT] {
val defaultValue: FT
class FieldSeq[+ST](val field: FT, val underlyingSeq: Seq[ST] = Vector())
extends SeqAdapter[ST,FieldSeq] {
def create[B](seq: Seq[B]) = new FieldSeq[B](field, seq)
}
object FieldSeq extends SeqAdapterCompanion[FieldSeq] {
def createDefault[A](seq: Seq[A]): FieldSeq[A] =
new FieldSeq[A](defaultValue, seq)
override implicit def canBuildFrom[A,B] = super.canBuildFrom[A,B]
}
}
This can be tested as so:
val StringField = new Field[String] { val defaultValue = "Default Value" }
StringField: java.lang.Object with Field[String] = $anon$1#57f5de73
val fs = new StringField.FieldSeq[Int]("str", Vector(1,2))
val fsfield = fs.field
fs: StringField.FieldSeq[Int] = (1, 2)
fsfield: String = str
val fm = fs.map(1 + _)
val fmfield = fm.field
fm: StringField.FieldSeq[Int] = (2, 3)
fmfield: String = str
val fs2 = new StringField.FieldSeq[Int]("str1", Vector(10, 20))
val fs2field = fs2.field
fs2: StringField.FieldSeq[Int] = (10, 20)
fs2field: String = str1
val ffor = for (x <- fs if x > 0; y <- fs2) yield (x + y)
val fforfield = ffor.field
ffor: StringField.FieldSeq[Int] = (11, 21, 12, 22)
fforfield: String = str
val smap = fs.map(_ + "!")
val smapfield = smap.field
smap: StringField.FieldSeq[String] = (1!, 2!)
smapfield: String = str
val break = List(1,2).map(1 + _)(collection.breakOut): StringField.FieldSeq[Int]
val breakfield = break.field
break: StringField.FieldSeq[Int] = (2, 3)
breakfield: String = Default Value
val x: StringField.FieldSeq[Any] = fs
val xfield = x.field
x: StringField.FieldSeq[Any] = (1, 2)
xfield: String = str
What is wrong is the following method?
def someMethod(funcs: => Option[String]*) = {
...
}
That actually "works" under 2.7.7 if you add parens:
scala> def someMethod(funcs: => (Option[String]*)) = funcs
someMethod: (=> Option[String]*)Option[String]*
except it doesn't actually work at runtime:
scala> someMethod(Some("Fish"),None)
scala.MatchError: Some(Fish)
at scala.runtime.ScalaRunTime$.boxArray(ScalaRunTime.scala:136)
at .someMethod(<console>:4)
at .<init>(<console>:6)
at .<clinit>(<console>) ...
In 2.8 it refuses to let you specify X* as the output of any function or by-name parameter, even though you can specify it as an input (this is r21230, post-Beta 1):
scala> var f: (Option[Int]*) => Int = _
f: (Option[Int]*) => Int = null
scala> var f: (Option[Int]*) => (Option[Int]*) = _
<console>:1: error: no * parameter type allowed here
var f: (Option[Int]*) => (Option[Int]*) = _
But if you try to convert from a method, it works:
scala> def m(oi: Option[Int]*) = oi
m: (oi: Option[Int]*)Option[Int]*
scala> var f = (m _)
f: (Option[Int]*) => Option[Int]* = <function1>
scala> f(Some(1),None)
res0: Option[Int]* = WrappedArray(Some(1), None)
So it's not entirely consistent.
In any case, you can possibly achieve what you want by passing in an Array and then sending that array to something that takes repeated arguments:
scala> def aMethod(os: Option[String]*) { os.foreach(println) }
aMethod: (os: Option[String]*)Unit
scala> def someMethod(funcs: => Array[Option[String]]) { aMethod(funcs:_*) }
someMethod: (funcs: => Array[Option[String]])Unit
scala> someMethod(Array(Some("Hello"),Some("there"),None))
Some(Hello)
Some(there)
None
If you really want to (easily) pass a bunch of lazily evaluated arguments, then you need a little bit of infrastructure that as far as I know doesn't nicely exist in the library (this is code for 2.8; view it as inspiration for a similar strategy in 2.7):
class Lazy[+T](t: () => T, lt: Lazy[T]) {
val params: List[() => T] = (if (lt eq null) Nil else t :: lt.params)
def ~[S >: T](s: => S) = new Lazy[S](s _,this)
}
object Lz extends Lazy[Nothing](null,null) {
implicit def lazy2params[T : Manifest](lz: Lazy[T]) = lz.params.reverse.toArray
}
Now you can easily create a bunch of parameters that are lazily evaluated:
scala> import Lz._ // To get implicit def
import Lz._
scala> def lazyAdder(ff: Array[()=>Int]) = {
| println("I'm adding now!");
| (0 /: ff){(n,f) => n+f()}
| }
lazyAdder: (ff: Array[() => Int])Int
scala> def yelp = { println("You evaluated me!"); 5 }
yelp: Int
scala> val a = 3
a: Int = 3
scala> var b = 7
b: Int = 7
scala> lazyAdder( Lz ~ yelp ~ (a+b) )
I'm adding now!
You evaluated me!
res0: Int = 15
scala> val plist = Lz ~ yelp ~ (a+b)
plist: Lazy[Int] = Lazy#1ee1775
scala> b = 1
b: Int = 1
scala> lazyAdder(plist)
I'm adding now!
You evaluated me!
res1: Int = 9
Evidently repeated arguments are not available for by-name parameters.