I\'m starting out with scala, and trying to apply the functional way to it, but I came out with bunch of nested if\\else constructions which is hard to read, and I wonder is
I don't think there is any reason to filter the list before you traverse it. You can just ignore the non parentheses as you traverse the list. I think it is also unnecessary to build the second list. All you really want to know is that the count of open parenthesis is never negative:
def balance(chars: List[Char]): Boolean = {
@tailrec
def _balance(chars: List[Char], count: Int) : Boolean =
chars match {
case Nil => count == 0 // end of the string did we close every open?
case '(' :: xs => _balance(xs, count+1)
case ')' :: xs => (count > 0) && _balance(xs, count-1)
case _ => _balance(chars.tail, count) // uninteresting char, skip it
}
_balance(chars, 0)
}
Well:
else if
conditions. tailrec
since it's tail-recursive. Set('(', ')')
, which is a function from Char
to Boolean
chars
is empty but parenthesis
is not.So it would look like:
def balance(chars: List[Char]): Boolean = {
@tailrec
def checkParentesys(chars: List[Char], parentesis: List[Char]): Boolean =
if (chars.isEmpty && parentesis.isEmpty)
true
else if (chars.head == '(')
checkParentesys(chars.tail, '(' :: parentesis)
else if (chars.isEmpty || parentesis.isEmpty)
false
else
checkParentesys(chars.tail, parentesis.tail)
checkParentesys(chars.filter(Set('(', ')')), List())
}
You could also just turn the whole thing into a pattern match:
def balance(chars: List[Char]): Boolean = {
@tailrec
def checkParentesys(chars: List[Char], parentesis: List[Char]): Boolean =
(chars, parentesis) match {
case (Nil, Nil) => true
case ('(' :: charsTail, _) => checkParentesys(charsTail, '(' :: parentesis)
case (Nil, _) => false
case (_, Nil) => false
case (')' :: charsTail, '(' :: parentesisTail) => checkParentesys(charsTail, parentesisTail)
}
checkParentesys(chars.filter(Set('(', ')')), List())
}
It might be nicer to write it as a fold:
def balance(chars: List[Char]): Boolean = chars.foldLeft(0){
case (0, ')') => return false
case (x, ')') => x - 1
case (x, '(') => x + 1
case (x, _ ) => x
} == 0
var parens :List[Char] = Nil
def matcher(chrs: List[Char]): Boolean = {
if (chrs.isEmpty) {
return parens.isEmpty
}
else {
chrs.head match {
case '(' => parens = '(' :: parens ;matcher(chrs.tail)
case ')' => if (parens.isEmpty) return false
else if (parens.apply(0) == '(') parens = parens.drop(1)
else return false;
matcher(chrs.tail);
case _ => matcher(chrs.tail)
}
}
}
As you can see I didn't use count because count won't work on ())(