编写flatten(lol: List[List[T]]): List[T]
非常容易,它会将列表列表转换为新列表。其他“扁平”收藏品(例如Set
)似乎也提供flatten
。
现在我想知道我是否可以为flatten
定义Tree[T]
(定义为T
和Tree[T]
s列表。
答案 0 :(得分:4)
这不是完美的,只是举个例子。您需要做的就是以深度优先或广度优先的方式遍历树并收集结果。与列表flatten
几乎相同。
1)定义一个树结构(我知道,我知道这不是最好的方法:)):
scala> case class Node[T](value: T, left: Option[Node[T]] = None,
| right: Option[Node[T]] = None)
defined class Node
2)创建一个小树:
scala> val tree = Node(13,
| Some(Node(8,
| Some(Node(1)), Some(Node(11)))),
| Some(Node(17,
| Some(Node(15)), Some(Node(25))))
| )
tree: Node[Int] = Node(13,Some(Node(8,Some(Node(1,None,None)),Some(Node(11,None,None)))),Some(Node(17,Some(Node(15,None,None)),Some(Node(25,None,None)))))
3)实现可以遍历树的函数:
scala> def observe[T](node: Node[T], f: Node[T] => Unit): Unit = {
| f(node)
| node.left foreach { observe(_, f) }
| node.right foreach { observe(_, f) }
| }
observe: [T](node: Node[T], f: Node[T] => Unit)Unit
4)用它来定义一个打印所有值的函数:
scala> def printall = observe(tree, (n: Node[_]) => println(n.value))
printall: Unit
5)最后,定义flatten
函数:
scala> def flatten[T](node: Node[T]): List[T] = {
| def flatten[T](node: Option[Node[T]]): List[T] =
| node match {
| case Some(n) =>
| n.value :: flatten(n.left) ::: flatten(n.right)
| case None => Nil
| }
|
| flatten(Some(node))
| }
flatten: [T](node: Node[T])List[T]
6)我们来测试一下。首先打印所有元素:
scala> printall
13
8
1
11
17
15
25
7)运行flatten
:
scala> flatten(tree)
res1: List[Int] = List(13, 8, 1, 11, 17, 15, 25)
这是一种像树遍历这样的通用树算法。我让它返回T
而不是Node
s,根据需要更改它。
答案 1 :(得分:3)
我不确定你想如何准确地定义它,但是你可以看看Scalaz Tree的实现:
https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Tree.scala
如果你想让flatten返回所有Tree节点的列表,那么Scalaz已经为你提供了你想要的东西:
def flatten: Stream[A]
结果类型是Stream而不是List,但我想这不是问题
如果您想要更复杂的东西,那么您可以使用现有的flatMap
:
def flatMap[B](f: A => Tree[B]): Tree[B]
假设您Tree
类型为Tree[Tree[A]]
,并希望将其展平为Tree[A]
:
def flatten1: Tree[A] = flatMap(identity)
这也适用于其他更奇怪的场景。例如,您可以拥有Tree[List[A]]
,并希望在不影响树结构本身的情况下展平Lists
内的所有内容:
def flatten2[B]: Tree[List[B]] = flatMap(l => leaf(l.flatten))
看起来它按预期工作:
scala> node(List(List(1)), Stream(node(List(List(2)), Stream(leaf(List(List(3, 4), List(5))))), leaf(List(List(4)))))
res20: scalaz.Tree[List[List[Int]]] = <tree>
scala> res20.flatMap(l => leaf(l.flatten)).drawTree
res23: String =
"List(1)
|
+- List(2)
| |
| `- List(3, 4, 5)
|
`- List(4)
"
值得注意的是,scalaz Tree也是Monad。如果您将查看scalaz / tests / src / test / scala / scalaz / TreeTest.scala,您将看到Tree实现了哪些法则:
checkAll("Tree", equal.laws[Tree[Int]])
checkAll("Tree", traverse1.laws[Tree])
checkAll("Tree", applicative.laws[Tree])
checkAll("Tree", comonad.laws[Tree])
我不知道为什么monad不在这里,但是如果你要添加checkAll("Tree", monad.laws[Tree])
并再次运行测试,它们就会通过。
答案 2 :(得分:2)
如果我正确理解了这个问题,你想要像这样定义树:
case class Tree[T]( value:T, kids:List[Tree[T]] )
首先,由于性能影响,我不想在解决方案中使用:::
。其次,我想做一些更通用的事情 - 为类型定义折叠运算符,可以用于各种事情 - 然后只需使用折叠来定义flatten
:
case class Tree[T]( value:T, kids:List[Tree[T]] ) {
def /:[A]( init:A )( f: (A,T) => A ):A =
( f(init,value) /: kids )( (soFar,kid) => ( soFar /: kid )(f) )
def flatten =
( List.empty[T] /: this )( (soFar,value) => value::soFar ).reverse
}
这是一个测试:
scala> val t = Tree( 1, List( Tree( 2, List( Tree(3,Nil), Tree(4,Nil) ) ), Tree(5,Nil), Tree( 6, List( Tree(7,Nil) ) ) ) )
t: Tree[Int] = Tree(1,List(Tree(2,List(Tree(3,List()), Tree(4,List()))), Tree(5,List()), Tree(6,List(Tree(7,List())))))
scala> t.flatten
res15: List[Int] = List(1, 2, 3, 4, 5, 6, 7)