为每个槽生成值的排列

时间:2013-11-19 00:21:10

标签: scala permutation for-comprehension

好吧,我必须在这里遇到一些愚蠢的事(对Scala来说是新手),在这种情况下道歉是否是微不足道的事。

我有一个类Action,它取一个名称(与问题无关)和一个列表参数。

每个参数都有一组可能的值,我想生成Action参数的可能参数的完整列表。我一直在尝试使用for-comprehension,而不是填写所有参数,我只是替换了一个值的组合。

我的代码看起来像这样(我简化它只是为了说明问题):

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)

    }
   }

函数replaceParam只返回一个参数替换为参数的动作。

所以如果我有一个带有参数A,B,C的action1,每个参数都可以取{a1,a2,a3},{b1,b2,b3}和{c1,c2,c3}(假设它们属于相同的类型),我最终得到:

action1(a1,B,C),action1(A,b1,C),action1(A,B,c1),action1(a2,B,C)等。

但我想要的是:

action1(a1,b1,c1),action1(a1,b1,c2),action1(a2,b1,c1)等。

实现这一目标最优雅的方法是什么?

1 个答案:

答案 0 :(得分:1)

以下是您问题的简单递归解决方案。但请注意,将可能的值作为Set传递可能会产生问题,因为这种方式对可能的值没有顺序,因此所有(并且不仅仅是一个)相应的参数将迭代所有值适当的参数类型。现在这应该按照你的要求做到:

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)