En primer lugar, creo y si se puede decir así, usted ha hecho un muy buen trabajo. Puedo sugerir un par de pequeños cambios a su código:
abstract class Tree
case class Node(value: Int, left: Tree, right: Tree) extends Tree
case object Nil extends Tree
- árbol no tiene que ser una clase caso, además de usar un caso de clase como nodo no hoja está en desuso debido a la posible errónea comportamiento de los métodos generados automáticamente
Nil
es un singleton y se define mejor como un caso-objeto en lugar de caso-clase.
Además consideran que la calificación de superclase Tree
con sealed
. sealed
le dice al compilador que la clase solo se puede heredar desde el mismo archivo fuente. Esto permite al compilador emitir advertencias siempre que la siguiente expresión de coincidencia no sea exhaustiva; en otras palabras, no incluye todos los casos posibles.
clase abstracta sellado árbol
El siguiente par de mejora podría ser hecha al sumTree
:
def sumTree(t: Tree) = {
// create a helper function to extract Tree value
val nodeValue: Tree=>Int = {
case Node(v,_,_) => v
case _ => 0
}
// parametrise fold with Tree to aid type inference further down the line
fold[Tree](t,Nil,(acc,l,r)=>Node(acc + nodeValue(l) + nodeValue(r) ,l,r))
}
nodeValue
función auxiliar también se puede definir como (la notación alternativa utilicé anterior es posible porque una secuencia de los casos entre llaves es tratado como una función literal):
def nodeValue (t:Tree) = t match {
case Node(v,_,_) => v
case _ => 0
}
pocas mejoras continuación se parametrizando fold
método con Tree
(fold[Tree]
). Debido a que Scala tipo inferer funciona a través de la expresión secuencialmente de izquierda a derecha, diciéndole temprano que vamos a tratar con Tree's, omite información de tipo al definir el literal de la función que se pasa al fold
más adelante.
Así que aquí es el código completo que incluye sugerencias:
sealed abstract class Tree
case class Node(value: Int, left: Tree, right: Tree) extends Tree
case object Nil extends Tree
object Tree {
def fold[B](t: Tree, e: B, n: (Int, B, B) => B): B = t match {
case Node(value, l, r) => n(value,fold(l,e,n),fold(r,e,n))
case _ => e
}
def sumTree(t: Tree) = {
val nodeValue: Tree=>Int = {
case Node(v,_,_) => v
case _ => 0
}
fold[Tree](t,Nil,(acc,l,r)=>Node(acc + nodeValue(l) + nodeValue(r) ,l,r))
}
}
La recursividad se le ocurrió es la única dirección posible que le permite recorrer el árbol y producir una copia modificada de la estructura de datos inmutables. Todos los nodos de hoja deben crearse antes de agregarse a la raíz, porque los nodos individuales del árbol son inmutables y todos los objetos necesarios para construir un nodo deben conocerse antes de la construcción: los nodos de hoja deben crearse antes de poder crear una raíz nodo.
Muchas gracias, especialmente el último bit de su respuesta. – roelio
@Vlad Esto fue realmente útil, pero realmente no entiendo por qué hay necesidad de un método 'val nodeValue: Tree => Int'. ¿Alguien puede explicar por qué tiene que hacerse de esta manera? – Sander
@Sander, 'nodeValue' abstrae el código repetitivo, si observa el código original en la pregunta, contiene dos expresiones de coincidencia independientes: primero para el subárbol izquierdo y luego para el derecho. En este punto, puede ser un poco difícil seguir el código, ya que la intención de los autores se confunde en detalle. Reemplazar el código repetitivo con la función de ayuda única que tiene un nombre descriptivo refleja mejor el intento y divide el código en unidades más manejables. Lo bueno de Scala es lo fácil que es agregar una pequeña función auxiliar y limitar su alcance al lugar de aplicación inmediato. –