我正在学习Scala并编写一个函数(递归)来计算括号的数量:+1表示打开,-1表示结束,以便匹配和平衡字符列表中的所有括号。如果括号是平衡的,它应该返回0。
我提出了这个问题(有许多印刷语句来了解发生了什么):
def countPar(charList: List[Char], count: Int): Int = {
if (count < 0) {
println("negative count, returning", count)
count
}
else if (charList.isEmpty) {
println("empty list, returning", count)
count
}
else if (charList.head.equals('(')) {
println("head is ", charList.head, " count + 1:", count + 1)
count + countPar(charList.tail, count + 1)
}
else if (charList.head.equals(')')) {
println("head is ", charList.head, " count - 1:", count - 1)
count + countPar(charList.tail, count - 1)
}
else {
println("head is ", charList.head, " count:", count)
countPar(charList.tail, count)
}
}
val parCount = countPar("(a(b)c)".toList, 0)
println(parCount)
打印语句做似乎确认逻辑正在运行,但最终的返回值是错误的:
(head is ,(, count + 1:,1)
(head is ,a, count:,1)
(head is ,(, count + 1:,2)
(head is ,b, count:,2)
(head is ,), count - 1:,1)
(head is ,c, count:,1)
(head is ,), count - 1:,0)
(empty list, returning,0)
parCount: Int = 4
我错过了什么?
答案 0 :(得分:4)
您目前正以非常强制/面向对象的方式编写代码。但这需要一种功能性的递归方法:
def isBalanced(string: List[Char], count: Int=0): Boolean = {
if (count < 0) { false } // We can only be balanced if ( precedes )
else {
string match {
case Nil => count == 0 // Is the count 0? If so, however we got here was balanced!
case '('::tail => isBalanced(tail, count + 1) // ( prepended to some list
case ')'::tail => isBalanced(tail, count - 1) // ) prepended to some list
case _::tail => isBalanced(tail, count)
}
}
}
请注意,我们的外部函数正在用它的签名回答问题:给定一个任意的字符列表,该列表是否平衡? (&#39;是或否&#39;暗示一个布尔值:使用整数会使这个函数的用户变得复杂。)与所有递归函数一样,函数询问是否可以简单地解决问题,如果不是,它会一点点的工作,然后简单地返回递归调用的结果。
为此,我们首先定义一个基本案例。也就是说,如果列表为空,我们只需返回计数是否为0.如果是,我们知道这些数据是平衡的。
其次,我们定义递归案例。在这里,我们相信isBalanced
返回正确的结果,并且只处理增量的不同。这两行代码处理:
case '('::tail => isBalanced(tail, count + 1)
case ')'::tail => isBalanced(tail, count - 1)
在每一个中,我们相应地增加或减少我们的计数。最后一行(case _::tail
)处理所有其他情况,这不应影响最终结果。
使用Scala强大的案例匹配功能(match
),这非常简单。我们可以在比赛前放一个简单的后卫,以确保一旦平衡消极,我们就会提前退出。然后,它是针对字符串输入的模式映射问题。这比使用无穷无尽的if-else子句更明确远。
另请注意创建默认参数值的技巧,因此您无需传入0
。这样可以让您重新使用该功能,从而阐明您的界面。
为了证明正确性:
isBalanced("((()))".toList) // true
isBalanced(")))(((".toList) // false
isBalanced("(()())".toList) // true
isBalanced("((()())".toList) // false
isBalanced("Foo () bar ()".toList) // true
关于打印日志行的说明:如果你需要这样做来追踪案例映射的发生方式,或者在案例条款中做任意体操,你可以这样做:
def isBalanced(string: List[Char], count: Int=0): Boolean = {
if (count < 0) { false }
else {
string match {
case Nil => count == 0
case '('::tail => {
println("Found an open paren")
isBalanced(tail, count + 1)
}
case ')'::tail => {
println("Found a close paren")
isBalanced(tail, count - 1)
}
case _::tail => isBalanced(tail, count)
}
}
}
答案 1 :(得分:2)
问题只是返回count + countPar(charList.tail, count + 1)
而不是countPar(charList.tail, count + 1)
(对于右括号也是如此)。
你的递归函数的意思是你根据头值更新计数,并将它传递给递归调用,它将根据尾值更新它(并且递归调用将执行相同的操作,直到尾巴是空的)。这意味着递归调用将返回正确的结果:无需向其添加任何内容。
编辑: 我认为一旦重构就会变得更加清晰(重要的部分是带注释的那个,我试着不改变你的方法):
def countPar(charList: List[Char], count: Int): Int = {
if (count < 0) {
println("negative count, returning", count)
count
} else if (charList.isEmpty) {
println("empty list, returning", count)
count
} else {
val updatedCount =
if (charList.head.equals('('))
count + 1
else if (charList.head.equals(')'))
count - 1
else
count
println(s"head is ${charList.head}, count: ${updatedCount}")
// We see here that the reursive call is the same in the 3 cases: the
// only difference is how we update the count
countPar(charList.tail, updatedCount)
}
}