3

I'm developing genetic algorithm and want to select case class randomly from following

sealed abstract class Gene(val code: Int)

object Gene {

  case object None extends Gene(0)

  case object Identity extends Gene(1)

  case object Squared extends Gene(2)

  case object Cubed extends Gene(3)

  case class Exp(a: Double) extends Gene(4)
}

How can I list up and select them randomly?

P.S.

Enum can't be used because I will implement parameters and methods to Gene

P.S.2

Following is I wrote, it needs manually edit when changing genes but it may be simple solution...

def getRandomGene(scaleFactor: Double): Gene = {
  Random.nextInt(5) match {
    case 0 => None
    case 1 => Identity
    case 2 => Squared
    case 3 => Cubed
    case 4 => Exp(scaleFactor / 10 + Random.nextDouble() * 9.9 * scaleFactor)
  }
}
Loran
  • 223
  • 2
  • 15

2 Answers2

1

Based on Travis Brown answer to the question "Iteration over a sealed trait in Scala" (https://stackoverflow.com/a/13672520/1637916) and my (poor) knowledge about scala macros, I wrote a simple implementation of select and random methods.

"select" method (from Sealed object) returns List of instances of case classes (or objects) which have constructor with specified parameters. "select" can not return one, random, implementation because it works at compile time and would return it always - for every invocation.

"random" method (from RanomElement object) picks one object from List returned by "select" method.

Sealed.scala file should be compiled first. If you try to compile two files (Sealed.scala and RandomGene.scala) at the same time, you will see the following error:

Error:(34, 27) macro implementation not found: select
(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)
      select[Gene]().random
  1. Sealed.scala contains select
import scala.language.experimental.macros
import scala.reflect.internal.Symbols
import scala.reflect.macros.whitebox

/**
  * Based on Travis Brown answer to the following question:
  * https://stackoverflow.com/questions/13671734/iteration-over-a-sealed-trait-in-scala (https://stackoverflow.com/a/13672520/1637916)
  */
object Sealed {

  def select[A](param: Any*): List[A] = macro select_impl[A]

  def select_impl[A: c.WeakTypeTag](c: whitebox.Context)(param: c.Expr[Any]*): c.Expr[List[A]] = {
    import c.universe._

    val symbol = weakTypeOf[A].typeSymbol

    if (!symbol.isClass) c.abort(
      c.enclosingPosition,
      "Can only enumerate values of a sealed trait or class."
    )
    else if (!symbol.asClass.isSealed) c.abort(
      c.enclosingPosition,
      "Can only enumerate values of a sealed trait or class."
    )
    else {
      val children = symbol.asClass.knownDirectSubclasses.toList

      val matchedChildren = children.filter { child =>
        child.asType.toType.members.filter(s => s.isConstructor && s.owner == child).exists {
          _.asMethod.paramLists match {
            case head :: Nil =>
              val constructorTypes = head.map(_.typeSignature)
              val paramTypes = param.map(_.actualType)

              constructorTypes.size == paramTypes.size && constructorTypes.zip(paramTypes).count { case (t1, t2) => t1 == t2 } == paramTypes.size
            case _ => false
          }
        }
      }

      c.Expr[List[A]] {
        def sourceModuleRef(sym: Symbol) = {
          if(param.nonEmpty) {
            Apply(
              Select(
                Ident(sym.companion),
                TermName("apply")
              ),
              param.map(_.tree).toList
            )
          } else {
            Ident(sym.asInstanceOf[Symbols#Symbol].sourceModule.asInstanceOf[Symbol])
          }
        }

        Apply(
          Select(
            reify(List).tree,
            TermName("apply")
          ),
          matchedChildren.map(sourceModuleRef(_))
        )
      }
    }
  }
}
  1. random
implicit class RandomSealed[A](ls: List[A]) {
  def random = ls(Random.nextInt(ls.size))
}
  1. example
import scala.language.experimental.macros
import scala.util.Random

object RandomGene {

  sealed abstract class Gene(val code: Int)

  object Gene {
    case object None extends Gene(0)

    case object Identity extends Gene(1)

    case object Squared extends Gene(2)

    case object Cubed extends Gene(3)

    case class Exp(a: Int, b: String) extends Gene(a)

    case class Exp1(b: String, a: Int) extends Gene(a)

    case class Exp2(a: Int) extends Gene(a)
    case class Exp3(a: Int) extends Gene(a)
    case class Exp4(a: Int) extends Gene(a)
  }

  implicit class RandomElement[A](ls: List[A]) {
    def random = ls(Random.nextInt(ls.size))
  }

  def main(args: Array[String]) {
    import Sealed._

    (1 to 5).foreach { n =>
      println(select[Gene]().random)
    }

    (1 to 5).foreach { n =>
      println(select[Gene](n).random)
    }
  }

}
Community
  • 1
  • 1
grzesiekw
  • 477
  • 4
  • 8
  • "Sealed.scala file should be compiled first."... I use IDE(intellij) and can it be possible? – Loran Nov 30 '15 at 01:55
  • Yes but you need to comment main method, build project, uncomment main and build it once again. – grzesiekw Nov 30 '15 at 05:46
  • You can also split your application into two modules, one with macro and one with usage of this macro. I made a simple project with such configuration: https://github.com/grzesiekw/select-random-sealed-example. – grzesiekw Nov 30 '15 at 09:14
-1

Make a collection of the apply methods of each and randomly select one and pass it the parameters

Something like List((Identity.apply _), (Square.apply _,...)