Scala返回值与预期不同

时间:2016-08-30 20:33:30

标签: scala recursion

我正在学习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

我错过了什么?

2 个答案:

答案 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)
  }
}