【问题标题】:Scala: Append element to list using cons methodScala:使用 cons 方法将元素附加到列表
【发布时间】:2018-12-09 11:32:51
【问题描述】:

阅读Programming in Scala, 3rd Edition时,它说

类列表确实提供了“追加”操作——它是这样写的:+ 但是这个 操作很少使用,因为附加到一个 列表随着列表的大小线性增长,而在前面加上 :: 需要恒定的时间。

如果你想通过附加元素有效地构建一个列表,你可以 把它们放在前面,当你完成后调用 reverse。

我试图理解,Scala 的惯用方式是什么?与 ListBuffer 相比,两次调用 List.reverse 是否可接受且高效(因为 ListBuffer 是可变的)?

// Scala Recommended way - but reverse twice?
val alist = List("A", "B")
// cons is O(1)
// This will print (A, B, C)
println(("C" :: alist.reverse).reverse)

// Scala also Recommended: Use ListBuffer
val alb = ListBuffer("A", "B")
alb.append("C")
val clist2 = alb.toList
// This will print (A, B, C)
println(clist2)

// DO NOT do this, its O(n)

val clist3 = alist :+ "C"
// This will print (A, B, C)
println(clist3)

P.S:我在这里不是指代码优化。一般推荐哪一种,不会收到WTH的表达。

【问题讨论】:

  • 8 个月前我从 java 搬到了 scala。我在前 6 个月编写的代码加载了可变集合。现在我从不使用它们并且非常关心性能。一旦你运行了真正的大数据,你就会自己明白为什么。这是一个自我发现的项目。现在,相信这些文件。 :)
  • 我对您在第三个代码 sn-p 上方的评论感到有些困惑。 “不要这样做,它的 O(n)”是什么意思?所有三个 sn-ps 都是 O(n)。 List.reverse 是 O(n),ListBuffer.toList 是 O(n),List.:+ 是 O(n)。
  • @JörgWMittag 我认为ListBuffer.toList 实际上是恒定时间(除非您在将 ListBuffer 转换为List 后重新使用它)
  • @joelb:你是对的。有趣的。因此,在调用 toList 之后改变缓冲区会延迟触发 O(n) 复制,但原始调用是 O(1)。聪明。
  • 这就是所有问题的答案。 docs.scala-lang.org/overviews/collections/…

标签: scala list idioms


【解决方案1】:

另一个实现可能是Difference Lists(也可以使用基于 Prolog 的解释 - Understanding Difference Lists)。

这就是我在 Scala 中实现 DList 的方式:

abstract class DiffList[A](calculate: List[A] => List[A]) {
  def prepend(s: List[A]): DiffList[A]

  def append(s: List[A]): DiffList[A]

  def result: List[A]
}

final class DiffListImpl[A](listFunc: List[A] => List[A])
    extends DiffList[A](listFunc) {
  def prepend(s: List[A]): DiffListImpl[A] =
    new DiffListImpl[A](listFunc andThen (s ++ _))


  def append(s: List[A]): DiffListImpl[A] =
    new DiffListImpl[A](listFunc andThen (_ ++ s))

  def result: List[A] = listFunc(Nil)
}

并使用它:

val l1 = List(1, 2)
val l2 = List(6, 7)
val l3 = List(3, 4, 5)
val dl = new DiffListImpl[Int](Nil)

val result = dl.prepend(l1).prepend(l2).append(l3).result

Result: List(6, 7, 1, 2, 3, 4, 5)

【讨论】:

  • 差异列表优化了小列表的大量串联操作,所以appendbody为了效率应该是new DiffListImpl[A](listFunc compose (s ++ _))
猜你喜欢
  • 2017-06-23
  • 2014-06-10
  • 2020-09-02
  • 2013-02-10
  • 1970-01-01
  • 2016-02-14
  • 2018-07-06
  • 1970-01-01
  • 2011-11-21
相关资源
最近更新 更多