0

I am starting the Scala programming course on Coursera, coming from 2 years experience with Java/Python/C#.

I am working through a problem where you must check to see if a list of characters has balanced paranthesis.

/**
* Exercise 2
*/
def balance(chars: List[Char]): Boolean = {

def recursiveBalance(chars: List[Char]): Boolean = {
  if (!chars.contains('(') && !chars.contains(')')) true
  else {
    val openIndex = chars.indexOf('(')
    if (openIndex == -1) false
    else {
      val chars2 = dropFirstMatch(chars, '(')
      val closeIndex = chars2.indexOf(')')
      if(closeIndex == -1 || openIndex > closeIndex) false
      else recursiveBalance(dropFirstMatch(chars2, ')'))
    }
  }
}

def remove(index: Int, list: List[Char]): List[Char] = {
  list.take(index) ++ list.drop(index)
}

def dropFirstMatch[A](ls: List[A], value: A): List[A] = {
  val index = ls.indexOf(value)  //index is -1 if there is no match
  if (index < 0) {
    ls
  } else if (index == 0) {
    ls.tail
  } else {
    // splitAt keeps the matching element in the second group
    val (a, b) = ls.splitAt(index)
    a ++ b.tail
  }
}
recursiveBalance(chars)

}

So this solution is working (if a little ugly). As part of the solution I attempted to remove an object from a list at a specific index. I've learned that in Scala Lists are immutable.

My attempt at doing this was to provide the index, the list, and use this example, which is the remove function. This works in the REPL, I made a list, ran the function and a new list was returned without the specified index.

But this did not work in my balance solution. Everytime the list was returned it was unchanged, causing infinite recursion. Eventually I stumbled on this article and borrowed their dropFirstMatch function, and upon substituting it, bam, working solution.

I am very new to Scala, and I must be overlooking something - can someone point out what it might be?

Community
  • 1
  • 1
erik-sn
  • 2,590
  • 1
  • 20
  • 37
  • 2
    BTW, this exercise has a much shorter solution using list's `head` and `tail` only, you might want to try finding it. – Tzach Zohar Mar 16 '16 at 07:17

2 Answers2

4

take(n) selects the first n elements in your list, where drop(n) selects all elements of the list except the first n ones.

To illustrate:

scala> val xs = List(0,1,2,3,4,5)
xs: List[Int] = List(0, 1, 2, 3, 4, 5)

scala> xs.take(2)
res0: List[Int] = List(0, 1)

scala> xs.drop(2)
res1: List[Int] = List(2, 3, 4, 5)

scala> xs.take(2) ++ xs.drop(2)
res2: List[Int] = List(0, 1, 2, 3, 4, 5)

In other words, your remove function simply returns the same list because it takes the first n elements of the original list, then adds that to the elements of the original list except the first n ones (drop). In order to remove the element at the given index in your list, you merely need to increment the index by one in your call to drop:

def remove(index: Int, list: List[Char]): List[Char] = {
  list.take(index) ++ list.drop(index+1)
}
Ton Torres
  • 1,509
  • 13
  • 24
2

Checking for balanced parenthesis is way easier than what you are doing:

  def balanced(list: Seq[Char]): Boolean  = list.foldLeft(0) { 
     case (n, _) if (n < 0) => return false
     case (n, '(') => n + 1
     case (n, ')') => n - 1
     case (n, _) => n
  } == 0

Or, if you are a purist, like some commenters, and insist on recursion:

  @tailrec
  def balanced(chars: Seq[Char],  n: Int = 0): Boolean = (n, chars) match {
    case (-1, _) => false
    case (n, Nil) => n == 0
    case ('(' :: tail, n) => balanced(tail, n+1)
    case (')' :: tail, n) => balanced(tail, n-1)
    case (_ :: tail, n) => balanced(tail, n)
  }
Dima
  • 39,570
  • 6
  • 44
  • 70
  • That's not enough. You also need to check that n is never less than 0 during the process. – Kolmar Mar 16 '16 at 11:52
  • @Kolmar Indeed. Updated. – Dima Mar 16 '16 at 12:27
  • Ugh, `return`... Just use a normal recursive function.. Folds are for when you intend to traverse the entire sequence... – dcastro Mar 16 '16 at 17:42
  • In fact, if I remember correctly, Odersky's style checker will deduce points from your solution if you use `return` – dcastro Mar 16 '16 at 17:44
  • @dcastro Let him "deduce" what he wants! I program for functionality, readability and efficiency, not for "points". – Dima Mar 16 '16 at 17:48
  • I only mentioned that because the OP is doing his course on Coursera, as he mentioned in his question, so this is VERY relevant to him. Also, if you care about readability, a fold isn't very idiomatic here. – dcastro Mar 16 '16 at 17:58
  • @dcastro without necessarily agreeing with you that "fold isn't idiomatic", I just have to point out that "readable" and "idiomatic" are not always equivalent too. Also, I don't know what "Coursera" is, and why it makes it any more relevant, but think, the, whatever it is, the point of learning should be gaining the skills needed to write good code, not earning "points". – Dima Mar 16 '16 at 18:01
  • True, but I do believe they're often correlated. I'm going to leave this link here as I think it's relevant and makes very good points: https://tpolecat.github.io/2014/05/09/return.html – dcastro Mar 16 '16 at 18:04
  • The link's title kinda promises to explain _why_ return should not be used, but then just goes into a long-winded explanation of what it does ... By the same logic, exceptions or `if` statements should be used as well ... and yes, you _could_ write a program without using either, but ... hopefully, common sense will prevail. – Dima Mar 16 '16 at 18:06
  • Actually, programming without exceptions is not only plausible, but also encouraged (enforced?) by the functional programming paradigm. – dcastro Mar 16 '16 at 19:33
  • @dcastro, yes, I know, and also functions, returning `Unit`, side effects, random numbers, input and output, etc. But like I said, common sense will prevail. Paradigm is a good thing but only so long as it does not get in the way of getting things done that are _actually_ useful. – Dima Mar 16 '16 at 20:02
  • Thank you for your solution, I hadn't been exposed to pattern matching yet but it seems very powerful. Also thank you for your philosophical arguments :) – erik-sn Mar 23 '16 at 15:09