Question

OK, I must be struggling with something stupid here (new to Scala), in which case apologies if it is something trivial.

I have a class Action which takes a name (irrelevant for the problem) and a list parameters.

Each parameter has a set of possible values, and I want to generate the full list of possible arguments for the parameters of the Action. I have been trying to use a for-comprehension but rather than filling in all arguments, I am getting combinations with just one value replaced.

My code looks something like this (I simplified it just to illustrate the problem):

case class ParamType(name: String)
{
  def matchesType(that: ParamType): Boolean = 
      this.name == that.name
}

sealed abstract class Param (paramType : ParamType)

case class VarParam(parameter : String, paramType : ParamType) extends Param (paramType)

case class ValueArg(name : String, paramType : ParamType) extends Param(paramType)


case class Action(name: String, params: List[Param])
{
   def possibleActions(possibleValues : Set[ValueArg]) : Set[Action] = 
     for {
   parameter <- params.collect({case pt: VarParam => pt})
   argument <- possibleValues
   if (argument.matchesType(parameter))
   } yield replaceParam(parameter, argument)

   def replaceParam(parameter: VarParam, argument: ValueArg) : Action =
   {
     val i = parameters.indexOf(parameter)
     if (i >= 0)
       Action(name, parameters.updated(i, argument)
   }      
}




   object tester {

    def main(args: Array[String]) {
       val possibleValues = Set[ValueArg](new ValueArg("a1", new ParamType("Atype")), 
                                       new ValueArg("a2", new ParamType("Atype")),
                                       new ValueArg("a3", new ParamType("Atype")),
                                       new ValueArg("b1", new ParamType("Btype")),
                                       new ValueArg("b2", new ParamType("Btype")),
                                       new ValueArg("b3", new ParamType("Btype")),
                                       new ValueArg("c1", new ParamType("Ctype")),
                                       new ValueArg("c2", new ParamType("Ctype")),
                                       new ValueArg("c3", new ParamType("Ctype")))

    val action1 = new Action("action1", List[Param](new VarParam("A", new ParamType("Atype")), new VarParam("B", new ParamType("Btype")), new VarParam("C", new ParamType("Ctype"))))

    val possibleActions = action1.possibleActions(possibleValues)

    println(possibleActions)

    }
   }

The function replaceParam just returns an action with the parameter replaced by the argument.

So if I have action1 with parameters A, B, C, each of which can take {a1, a2, a3}, {b1, b2, b3} and {c1, c2, c3} (assume they are of the same type), I am ending up with:

action1(a1, B, C) , action1(A, b1, C), action1(A, B, c1), action1(a2, B, C) etc.

But what I want is:

action1(a1, b1, c1), action1(a1, b1, c2), action1(a2, b1, c1) etc.

What is the most elegant way to achieve this?

Was it helpful?

Solution

The following is a simple recursive solution to your problem. However, note that passing the possible values as a Set might turn out to be problematic because this way there is no order on the possible values and hence all (and not only one in particular) corresponding parameters will iterate over all values with suitable parameter types. Now this should do what you asked:

case class ParamType(name: String) {
  def matchesType(that: ParamType): Boolean =
    this.name == that.name
}

sealed abstract class Param(val paramType: ParamType)
case class VarParam(parameter: String, override val paramType: ParamType) extends Param(paramType)
case class ValueArg(name: String, override val paramType: ParamType) extends Param(paramType)

case class Action(name: String, params: List[Param]) {
  lazy val varParams: List[Param] =
    params collect {
      case pt: VarParam => pt
    }

  def possibleActions(possibleValues: Set[ValueArg]): Set[Action] = {
    val pvs  = possibleValues groupBy (_.paramType)
    def aux(acc: List[Param], rem: List[Param]): Set[Action] = rem match {
      case Nil => Set(Action(name, acc.reverse))
      case x :: xs => for {
        pv <- pvs(x.paramType) if pvs.contains(x.paramType)
        rest <- aux(pv :: acc, xs)
      } yield rest
    }
    aux(Nil, varParams)
  }
}

val possibleValues = Set(
  ValueArg("a1", ParamType("Atype")),
  ValueArg("a2", ParamType("Atype")),
  ValueArg("a3", ParamType("Atype")),
  ValueArg("b1", ParamType("Btype")),
  ValueArg("b2", ParamType("Btype")),
  ValueArg("b3", ParamType("Btype")),
  ValueArg("c1", ParamType("Ctype")),
  ValueArg("c2", ParamType("Ctype")),
  ValueArg("c3", ParamType("Ctype")))

val action1 = Action("action1",
  List(VarParam("A", ParamType("Atype")),
    VarParam("B", ParamType("Btype")),
    VarParam("C", ParamType("Ctype"))))

val possibleActions = action1.possibleActions(possibleValues)
assert(possibleActions.size == 3 * 3 * 3)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top