flatten函数是一个函数,它获取列表列表并返回一个列表,该列表是所有列表的串联。作为functional programming in scala的练习,我们必须以线性复杂度实现该功能。我的解决方案是:
def flatten[A](l: List[List[A]]): List[A] = {
def outer(ll: List[List[A]]):List[A] = {
ll match {
case Nil => Nil
case Cons(h,t) => inner(t, h)
}
}
def inner(atEnd: List[List[A]], ll: List[A]): List[A] = {
ll match {
case Nil => outer(atEnd)
case Cons(h,t) => Cons(h, inner(atEnd, t))
}
}
outer(l)
}
有效。现在我查看了solution proposed:
def append[A](a1: List[A], a2: List[A]): List[A] =
a1 match {
case Nil => a2
case Cons(h,t) => Cons(h, append(t, a2))
}
def flatten2[A](l: List[List[A]]): List[A] =
foldRight(l, Nil:List[A])(append)
我怀疑flatten2
是否真的是线性的。在foldLeft
的每次迭代中,调用函数append
。该函数将解析累加器的所有节点。第一次,累加器是Nil
,第二次是l.get(1)
然后是l.get(1) + l.get(2)
...所以l
中的第一个列表不会只交叉一次,但是l.length - 1
直到函数结束。我是对的吗?
虽然我的实现确实只跨越每个列表一次。我的实施真的更快吗?
答案 0 :(得分:2)
考虑例如flatten2 (List(List(1,2,3), List(4,5), List(6)))
,扩展为:
append(List(1,2,3),
append(List(4,5),
append(List(6),
Nil)))
正如a comment in the link所说,“append
需要时间与其第一个参数成比例”,因此“此函数在所有列表的总长度中是线性的”。 (另一方面,flatten2
和flatten
都不是尾递归的。)