从公式收集变量的首选方法

时间:2013-11-19 16:45:17

标签: scala pattern-matching

我正在处理命题逻辑,我编写了两个算法来收集公式中的所有变量。我希望输出是不可变的。在速度/优雅方面哪一个应该首选?还有更好的方法吗?提前谢谢。

def getVariables(formula: Formula): Set[Variable] = formula match {
 case v: Variable => HashSet(v)
 case Negation(f) => getVariables(f)
 case BinaryConnective(f0, f1) => getVariables(f0) ++ getVariables(f1)
 case _ => HashSet.empty[Variable]
}

def getVariables2(formula: Formula): Set[Variable] = {
 def getVariables2(formula: Formula, set: mutable.HashSet[Variable]): Unit = formula match {
   case v: Variable => set += v
   case Negation(f) => getVariables2(f, set)
   case BinaryConnective(f0, f1) => getVariables2(f0, set); getVariables2(f1, set)
   case _ =>
 }
 val set = mutable.HashSet.empty[Variable]
 getVariables2(formula, set)
 set.toSet
}

1 个答案:

答案 0 :(得分:1)

最快的方法几乎总是使用建设者。所以,假设没有堆栈溢出:

def getVars(formula: Formula): Set[Variable] = {
  val sb = Set.newBuilder[Variable]
  def inner(formula: Formula) { formula match {
    case v: Variable => sb += v
    case Negation(f) => inner(f)
    case BinaryConnective(f0, f1) => inner(f0); inner(f1)
    case _ =>
  }}
  inner(formula)
  sb.result
}

然而,你的第一个版本可能是最优雅的。

请注意,如果您可能有非常大的公式,则此递归解决方案可能存在堆栈溢出的危险。修复相对简单:

def getVars2(formula: Formula): Set[Variable] = {
  val sb = Set.newBuilder[Variable]
  def inner(formulas: List[Formula]) { 
    var more: List[Formula] = Nil
    formulas.foreach{ _ match {
      case v: Variable => sb += v
      case Negation(f) => more = f :: more
      case BinaryConnective(f0, f1) => more = f1 :: f0 :: more
      case _ =>
    }}
    if (!more.isEmpty) inner(more)
  }
  inner(formula :: Nil)
  sb.result
}

你的名字太长了,无法方便地输入一个有趣的非微小表达式,但如果我们缩写为大写字母,那么:

BC(N(V('x)), BC(BC(V('a),V('x)),V('y)))
使用getVars

比第一个解决方案快约7倍; getVars2稍慢(仅快4倍)。

(基准时间是:

getVariables   1380 ns  +-  20 ns
getVars         190 ns  +-  10 ns
getVars2        360 ns  +-  10 ns