Scala: Make sure braces are balanced

孤者浪人 提交于 2019-12-01 21:36:30

Here is a version:

def balance(chars: List[Char]): Boolean = {
    def inner(c: List[Char], count: Int): Boolean = c match {
        case Nil                   => count == 0           // Line 1
        case ')' :: _ if count < 1 => false                // Line 2
        case ')' :: xs             => inner(xs, count - 1) // Line 3
        case '(' :: xs             => inner(xs, count + 1) // Line 4
        case _ :: xs               => inner(xs, count)     // Line 5
    }
    inner(chars, 0)
}

So in your code, I think you are missing the additional check for count < 1 when you encounter the right paranthesis! So you need an additional else if that checks for both the ')' and count < 1 (Line 2 in the example code above)

You've made a very simple and completely understandable mistake. The parentheses in )( are balanced, by your current definition. It's just that they're not balanced in the way we would usually think. After the first character, you have -1 unclosed parentheses, and then after the second characte we're back to 0, so everything is fine. If the parenthesis count ever drops below zero, the parentheses cannot possibly be balanced.

Now there are two real ways to handle this. The quick and dirty solution is to throw an exception.

case object UnbalancedException extends Exception

if (i < 0)
  throw UnbalancedException

then catch it and return false in balance.

try {
  ... // find() call goes in here
} catch {
  case UnbalancedException => false
}

The more functional solution would be to have find return an Option[Int]. During the recursion, if you ever get a None result, then return None. Otherwise, behave as normally and return Some(n). If you ever encounter the case where i < 0 then return None to indicate failure. Then in balance, if the result is nonzero or the result is None, return false. This can be made prettier with for notation, but if you're just starting out then it can be very helpful to write it out by hand.

You can also use the property of Stack data structure to solve this problem. When you see open bracket, you push it into the stack. When you see close bracket, you pop from the stack (instead of Stack I'm using List, because immutable Stack is deprecated in Scala):

def isBalanced(chars: Seq[Char]): Boolean = {
  import scala.annotation.tailrec

  case class BracketInfo(c: Char, idx: Int)
  def isOpen(c: Char): Boolean = c == '('
  def isClose(c: Char): Boolean = c == ')'
  def safePop[T](stack: List[T]): Option[T] = {
    if (stack.length <= 1) stack.headOption
    else stack.tail.headOption
  }

  @tailrec
  def isBalanced(chars: Seq[Char], idx: Int, stack: List[BracketInfo]): Boolean = {
    chars match {
      case Seq(c, tail@_*) =>
        val newStack = BracketInfo(c, idx) :: stack // Stack.push
        if (isOpen(c)) isBalanced(tail, idx + 1, newStack)
        else if (isClose(c)) {
          safePop(stack) match {
            case Some(b) => isBalanced(tail, idx + 1, stack.tail)
            case None =>
              println(s"Closed bracket '$c' at index $idx was not opened")
              false
          }
        }
        else isBalanced(tail, idx + 1, stack)
      case Seq() =>
        if (stack.nonEmpty) {
          println("Stack is not empty => there are non-closed brackets at positions: ")
          println(s"${stack.map(_.idx).mkString(" ")}")
        }
        stack.isEmpty
    }
  }

  isBalanced(chars, 0, List.empty[BracketInfo])
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!