Scala способ программирования группы if

i'am, начинающийся с scala, и попробуйте применить к нему функциональный путь, но я вышел с кучей вложенных if\else конструкций, которые трудно читать, и мне интересно, есть ли более хороший способ программирования таких вещи? Например, я написал script, который выполняет балансировку скобок

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

Можете ли вы предложить, как я могу написать его более функциональным и более scala как?

Ответы

Ответ 1

Лучше написать его как складку:

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

Ответ 2

Я не думаю, что есть какие-то причины для фильтрации списка, прежде чем вы пройдете его. Вы можете просто игнорировать скобки, когда вы проходите список. Я думаю, что также нет необходимости строить второй список. Все, что вы действительно хотите знать, это то, что количество открытых круглых скобок никогда не является отрицательным:

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

Ответ 3

Ну:

  • Вы можете начать с написания с условиями else if.
  • Продолжайте аннотировать его с помощью tailrec, поскольку он является хвостовым рекурсивным.
  • Условие фильтра можно записать просто как Set('(', ')'), которое является функцией от Char до Boolean
  • Я думаю, что вам не хватает условия, когда chars пуст, но parenthesis нет.

Итак, это будет выглядеть так:

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

Вы также можете просто превратить все это в соответствие шаблону:

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

Ответ 4

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

Как вы можете видеть, я не использовал счет, потому что счетчик не будет работать()) (