我正在学习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))
}
答案 0 :(得分:4)
当与管道(|)匹配模式时,不允许绑定除通配符(_)以外的任何变量。
这很容易理解:在case
的正文中,bh
或br
的实际类型是什么,例如,如果您的两种选择符合不同的类型?
编辑 - 来自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)
}