Reputation: 800
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 there nicer way to program such things?
For example I wrote a script, that performs parentheses balancing:
def balance(chars: List[Char]): Boolean = {
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 (parentesis.isEmpty)
false
else
checkParentesys(chars.tail, parentesis.tail)
checkParentesys(chars.filter(s => s == '(' || s == ')'), List())
}
How can I write it to be more functional and more scala like?
Upvotes: 16
Views: 9583
Reputation: 31
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 ())(
Upvotes: 3
Reputation: 11366
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)
}
Upvotes: 19
Reputation: 51109
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
Upvotes: 27
Reputation: 52701
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())
}
Upvotes: 4