Scala模式匹配与析取不起作用

时间:2014-05-01 12:46:12

标签: scala

我正在学习Scala,并且不明白为什么以下内容无效。

我想重构(测试的)mergeAndCount函数,它是计数反转算法的一部分,以利用模式匹配。这是一个未经重构的方法:

def mergeAndCount(b: Vector[Int], c: Vector[Int]): (Int, Vector[Int]) = {
  if (b.isEmpty && c.isEmpty)
    (0, Vector())
  else if (!b.isEmpty && (c.isEmpty || b.head < c.head)) {
    val (count, r) = mergeAndCount(b drop 1, c)
    (count, b.head +: r)
  } else {
    val (count, r) = mergeAndCount(b, c drop 1)
    (count + b.length, c.head +: r)
  }
}

这是我重构的方法mergeAndCount2。哪个工作正常。

def mergeAndCount2(b: Vector[Int], c: Vector[Int]): (Int, Vector[Int]) = (b, c) match {
  case (Vector(), Vector()) =>
    (0, Vector())
  case (bh +: br, Vector()) =>
    val (count, r) = mergeAndCount2(br, c)
    (count, bh +: r)
  case (bh +: br, ch +: cr) if bh < ch =>
    val (count, r) = mergeAndCount2(br, c)
    (count, bh +: r)
  case (_, ch +: cr) =>
    val (count, r) = mergeAndCount2(b, cr)
    (count + b.length, ch +: r)
}

然而,你可以看到第二和第三种情况是重复的代码。因此,我想使用这样的分离结合它们:

  case (bh +: br, Vector()) | (bh +: br, ch +: cr) if bh < ch =>       
    val (count, r) = mergeAndCount2(br, c)
    (count, bh +: r)

这给了我一个错误(在案例行上):illegal variable in pattern alternative

我做错了什么?

非常感谢任何帮助(也有关于风格)。

更新:感谢您的建议,这是我的结果:

@tailrec
def mergeAndCount3(b: Vector[Int], c: Vector[Int], acc : (Int, Vector[Int])): (Int, Vector[Int]) = (b, c) match {
  case (Vector(), Vector())  =>
    acc
  case (bh +: br, _) if c.isEmpty ||  bh < c.head =>
    mergeAndCount3(br, c, (acc._1, acc._2 :+ bh))
  case (_, ch +: cr) =>
    mergeAndCount3(b, cr, (acc._1 + b.length, acc._2 :+ ch))
}

3 个答案:

答案 0 :(得分:4)

当与管道(|)匹配模式时,不允许绑定除通配符(_)以外的任何变量。

这很容易理解:在case的正文中,bhbr的实际类型是什么,例如,如果您的两种选择符合不同的类型?

编辑 - 来自scala参考:

  

8.1.11模式替代语法:Pattern :: = Pattern1 {'|'Pattern1}模式替代p 1 | 。 。 。 | p n由a组成   替代模式的数量p i。所有替代模式都是类型   检查模式的预期类型。他们可能没有约束力   除通配符之外的变量。替代模式匹配a   如果至少有一个替代品与v匹配,则为v。

首次评论后编辑 - 您可以使用通配符来匹配此类内容,例如:

try {
 ...
} catch {
  case (_: NullPointerException | _: IllegalArgumentException) => ...
}

答案 1 :(得分:3)

如果你考虑一下,看看你的case子句,编译器应该如何知道在案例正文中是否应该允许使用 ch cr

这类问题使得编译器很难在同一个case子句中支持析取和变量绑定,因此根本不允许这样做。

您的mergeAndCount2函数在模式匹配方面看起来非常好。我认为它最明显的问题不是尾递归,因此不能在恒定的堆栈空间中运行。如果你能解决这个问题,你可能会以一些重复性较低的东西结束。

答案 2 :(得分:2)

您可以重写案例表达式并将析取移动到if部分

case (bh +: br, cr) if cr.isEmpty || bh < cr.head =>
  val (count, r) = mergeAndCount2(br, c)
  (count, bh +: r)

<强>更新

你可以简化一下:

@tailrec
def mergeAndCount3(b: Vector[Int], c: Vector[Int],
    count: Int = 0, r: Vector[Int] = Vector()): (Int, Vector[Int]) = 
  (b, c) match {
    case (bh +: br, _) if c.isEmpty || bh < c.head =>
      mergeAndCount3(br, c, count, bh +: r)
    case (_, ch +: cr) =>
      mergeAndCount3(b, cr, count + b.length, ch +: r)
    case _ => (count, r)
  }