首页 文章

创建Scala列表的首选方法

提问于
浏览
114

有几种方法可以在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)
}

9 回答

  • 22

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

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

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

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

    如果你按照相同的顺序获得元素,那么如果性能至关重要,那么 ListBuffer 很可能是一个更好的选择 .

    但是,如果您没有处于关键路径并且输入足够低,您可以在以后的列表中始终 reverse ,或者只是 foldRightreverse 输入,这是线性时间 .

    DON'T 做的是使用 List 并追加它 . 这将使您的性能远远低于最后的前置和后退 .

  • 2

    对于简单的情况:

    val list = List(1,2,3)
    

    :)

  • 1

    嗯......这些对我来说似乎太复杂了 . 我可以提议吗?

    def listTestD = (0 to 3).toList
    

    要么

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

    您希望通过消除任何变量来关注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)
    

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

    索引的seq将包含您需要的一切:

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

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

    我总是喜欢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” .

  • 107

    使用 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)
    
  • 64

    Note: This answer is written for an old version of Scala.

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

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

    A PDF document describing the proposed changes of the collection classes

  • 5

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

    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")
        }
      }
    }
    
  • 2

    只是一个使用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)
    

相关问题