2012-09-21 10 views
15

estoy comenzando con scala, y trato de aplicarlo de manera funcional, pero saqué un montón de construcciones if \ if anidadas que son difíciles de leer, y me pregunto si hay alguna mejor forma de programar tales cosas? Por ejemplo he escrito guión, que realiza el equilibrio de paréntesisScala manera de programar el grupo de ifs

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

puede sugerir, cómo puedo escribir Scala más funcional y más como?

+31

No sea tímido, solo diga que esta pregunta es del curso de scara 's coursera – AndreasScheinert

+0

¿hace la diferencia? He hecho la asignación, en alcance del material proporcionado, y preguntándose r mejor solución. – Pilgrim

+5

Hace la diferencia, ya que acaba de violar el Código de Honor de coursera al publicar una respuesta (consulte https://www.coursera.org/maestro/auth/normal/tos.php#honorcode rule 3) – Frank

Respuesta

25

Puede ser que sea más agradable para escribir como un pliegue:

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 
+0

heh) el resultado es cada vez más corto) gracias, estoy emocionado de ver todas esas opciones, parece que la programación funcional es mucho más opciones que la programación imperativa – Pilgrim

+2

Pensé que usar "volver" era un mal estilo? – Ilan

+2

No siempre. Los principiantes, especialmente los principiantes que vienen de Java, tienden a abusar violentamente del 'retorno'. Pero, aquí 'return' es necesario para salir temprano del redil. Hay otras formas de resolver esto, por supuesto (ver otras respuestas), pero este me parece bastante elegante, incluso con el 'retorno'. –

3

Bueno:

  1. Se podría empezar por escribir con else if condiciones.
  2. Continúe anotándolo con tailrec ya que es recursivo en la cola.
  3. La condición de filtro se puede escribir más simplemente como Set('(', ')'), que es una función Char-Boolean
  4. Creo que se está perdiendo la condición en la que chars está vacía, pero no es parenthesis.

Por lo que se vería así:

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

También puedes, simplemente convertir todo el asunto en una coincidencia de patrón:

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

Gracias por Set y tailrec. Entonces, no hay forma de reescribir la otra lógica para que sea más legible. De lo contrario, si no ayuda mucho porque, en mi opinión, confunde a quién pertenece y rompe la lógica. Pensé que podría combinar cheques múltiples de alguna forma en una expresión. – Pilgrim

+0

Sí, eso es lo que busco, no te vi en la segunda parte del comentario – Pilgrim

+0

@ Pilgrim, creo que la mayoría de los programadores de cualquier idioma dirían que el 'else if' es * más * legible ya que le da al lector una lista de condiciones Además, definitivamente * no * rompe la lógica; la lógica es idéntica. – dhg

17

No creo que haya ninguna razón para filtrar la lista antes de atravesarla Simplemente puede ignorar los paréntesis no a medida que atraviesa la lista. Creo que tampoco es necesario construir la segunda lista. Todo lo que quiere saber es que el recuento de paréntesis abierto nunca es negativo:

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

Gracias, eso suena razonable – Pilgrim

+2

En lugar de '_balance' usar' loop' o 'go' o algo más. El primero no se ajusta a la guía de estilo no oficial. – sschaef

+0

La manera estándar está llamando 'balance0' creo – ron

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

Como se puede ver que no usó el conteo porque el recuento no funcionará en()) (

Cuestiones relacionadas