erik-sn
erik-sn

Reputation: 2600

Scala Removing Item from List - Comparison of Methods

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?

Upvotes: 0

Views: 2125

Answers (2)

Dima
Dima

Reputation: 40510

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

Upvotes: 2

Ton Torres
Ton Torres

Reputation: 1529

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

Upvotes: 4

Related Questions