创建Scala列表的首选方法

时间:2009-08-06 20:21:05

标签: scala

有几种方法可以在Scala中构造不可变列表(请参阅下面的设计示例代码)。您可以使用可变的ListBuffer,创建var列表并对其进行修改,使用tail recursive方法,以及其他我不了解的方法。

本能地,我使用ListBuffer,但我没有充分的理由这样做。有没有首选或惯用的方法来创建列表,还是有一种方法最适合一种方法而不是另一种方法?

import scala.collection.mutable.ListBuffer

// THESE are all the same as: 0 to 3 toList.
def listTestA() ={
    var list:List[Int] = Nil

    for(i <- 0 to 3) 
        list = list ::: List(i)
    list
}


def listTestB() ={
    val list = new ListBuffer[Int]()

    for (i <- 0 to 3) 
        list += i
    list.toList
}


def listTestC() ={
    def _add(l:List[Int], i:Int):List[Int] = i match {
        case 3 => l ::: List(3)
        case _ => _add(l ::: List(i), i +1)
    }
    _add(Nil, 0)
}

10 个答案:

答案 0 :(得分:107)

ListBuffer是一个可变列表,具有常量时间追加和恒定时间转换为List

List是不可变的,并且具有常量时间前置和线性时间追加。

如何构建列表取决于您将使用列表的算法以及获取元素的顺序。

例如,如果您按照与使用时相反的顺序获取元素,那么您可以使用List并执行前置。你是否会使用尾递归函数,foldLeft或其他东西并不真正相关。

如果您使用相同顺序的元素,那么如果性能至关重要,则ListBuffer最有可能成为首选。

但是,如果您没有处于关键路径且输入足够低,您可以随后reverse列表,或仅foldRightreverse输入,是线性时间。

不要做的是使用List并附加到它。这将使您的性能远远低于最后的前置和后退。

答案 1 :(得分:65)

对于简单的案例:

val list = List(1,2,3) 

:)

答案 2 :(得分:22)

呃......这些对我来说似乎太复杂了。我可以提议

def listTestD = (0 to 3).toList

def listTestE = for (i <- (0 to 3).toList) yield i

答案 3 :(得分:5)

您希望通过消除任何变量来关注Scala中的不变性。 可读性对你的同伴来说仍然很重要:

尝试:

scala> val list = for(i <- 1 to 10) yield i
list: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

在大多数情况下,您可能甚至不需要转换为列表:)

索引序列将包含您需要的所有内容:

也就是说,您现在可以处理IndexedSeq:

scala> list.foldLeft(0)(_+_)
res0: Int = 55

答案 4 :(得分:2)

我总是喜欢List而我在“for comprehension”之前使用“fold / reduce”。但是,如果需要嵌套的“折叠”,则“理解”是优选的。如果我无法使用“fold / reduce / for”完成任务,则递归是最后的手段。

所以对于你的例子,我会这样做:

((0 to 3) :\ List[Int]())(_ :: _)
在我做之前

(for (x <- 0 to 3) yield x).toList

注意:由于“_”的顺序,我在这里使用“foldRight(:\)”而不是“foldLeft(/ :)”。对于不抛出StackOverflowException的版本,请改用“foldLeft”。

答案 5 :(得分:2)

注意:此答案是针对旧版本的Scala编写的。

Scala集合类将从Scala 2.8开始重新设计,因此请准备好尽快更改创建列表的方式。

创建List的前向兼容方式是什么?我不知道,因为我还没有读过2.8文档。

A PDF document describing the proposed changes of the collection classes

答案 6 :(得分:2)

使用List.tabulate,像这样,

List.tabulate(3)( x => 2*x )
res: List(0, 2, 4)

List.tabulate(3)( _ => Math.random )
res: List(0.935455779102479, 0.6004888906328091, 0.3425278797788426)

List.tabulate(3)( _ => (Math.random*10).toInt )
res: List(8, 0, 7)

答案 7 :(得分:1)

作为一名新的scala开发人员,我使用上面建议的方法编写了一个小测试来检查列表创建时间。看起来(对于(p < - (0到x))产生p)来列出最快的方法。

import java.util.Date
object Listbm {

  final val listSize = 1048576
  final val iterationCounts = 5
  def getCurrentTime: BigInt = (new Date) getTime

  def createList[T] ( f : Int => T )( size : Int ): T = f ( size )

  // returns function time execution
  def experiment[T] ( f : Int => T ) ( iterations: Int ) ( size :Int ) : Int  = {

    val start_time = getCurrentTime
    for ( p <- 0 to iterations )  createList ( f ) ( size )
    return (getCurrentTime - start_time) toInt

  }

  def printResult ( f:  => Int ) : Unit = println ( "execution time " + f  )

  def main( args : Array[String] ) {


    args(0) match {

      case "for" =>  printResult ( experiment ( x => (for ( p <- ( 0 to x ) ) yield p) toList  ) ( iterationCounts ) ( listSize ) )
      case "range"  =>  printResult ( experiment ( x => ( 0 to x ) toList ) ( iterationCounts ) ( listSize ) )
      case "::" => printResult ( experiment ( x => ((0 to x) :\ List[Int]())(_ :: _) ) ( iterationCounts ) ( listSize ) )
      case _ => println ( "please use: for, range or ::\n")
    }
  }
}

答案 8 :(得分:0)

只是一个使用collection.breakOut

的示例
scala> val a : List[Int] = (for( x <- 1 to 10 ) yield x * 3)(collection.breakOut)
a: List[Int] = List(3, 6, 9, 12, 15, 18, 21, 24, 27, 30)

scala> val b : List[Int] = (1 to 10).map(_ * 3)(collection.breakOut)
b: List[Int] = List(3, 6, 9, 12, 15, 18, 21, 24, 27, 30)

答案 9 :(得分:0)

要创建字符串列表,请使用以下命令:

val l = List("is", "am", "are", "if")