software - scala tutorial
Forma preferida de crear una lista de Scala (9)
Como nuevo desarrollador de scala, escribí una pequeña prueba para verificar el tiempo de creación de la lista con los métodos sugeridos anteriormente. Parece que (para (p <- (0 a x)) rinda p) para enumerar el enfoque más rápido.
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")
}
}
}
Hay varias maneras de construir una lista inmutable en Scala (consulte el código de ejemplo artificial a continuación). Puedes usar un ListBuffer mutable, crear una lista var
y modificarla, usar un método recursivo de cola y probablemente otros que no conozca.
Instintivamente, uso el ListBuffer, pero no tengo una buena razón para hacerlo. ¿Existe un método preferido o idiomático para crear una lista, o hay situaciones que son mejores para un método que para otro?
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)
}
Desea centrarse en la inmutabilidad en Scala en general mediante la eliminación de cualquier vars. La legibilidad sigue siendo importante para su prójimo, así que:
Tratar:
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)
Probablemente ni siquiera necesite convertir a una lista en la mayoría de los casos :)
El seq indexado tendrá todo lo que necesita:
Es decir, ahora puede trabajar en IndexedSeq:
scala> list.foldLeft(0)(_+_)
res0: Int = 55
Siempre prefiero List y uso "fold / reduce" antes de "para comprensión". Sin embargo, se prefiere "para la comprensión" si se requieren "pliegues" anidados. La recursividad es el último recurso si no puedo lograr la tarea usando "fold / reduce / for".
entonces para su ejemplo, lo haré:
((0 to 3) :/ List[Int]())(_ :: _)
antes que yo:
(for (x <- 0 to 3) yield x).toList
Nota: utilizo "foldRight (: /)" en lugar de "foldLeft (/ :)" aquí debido al orden de "_" s. Para una versión que no arroja Exception, use "foldLeft" en su lugar.
Uhmm ... me parecen demasiado complejos. ¿Puedo proponer?
def listTestD = (0 to 3).toList
o
def listTestE = for (i <- (0 to 3).toList) yield i
Usando List.tabulate
, así,
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)
Y para casos simples:
val list = List(1,2,3)
:)
solo un ejemplo que usa 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)
ListBuffer
es una lista mutable que tiene un ListBuffer
de tiempo constante y una conversión de tiempo constante en una List
.
List
es inmutable y tiene un prepend de tiempo constante y un apendido de tiempo lineal.
La forma en que construyas tu lista depende del algoritmo para el que usarás la lista y del orden en el que obtienes los elementos para crearla.
Por ejemplo, si obtienes los elementos en el orden opuesto de cuándo van a usarse, entonces puedes simplemente usar una List
y hacerlos antes. Ya sea que lo haga con una función recursiva de cola, foldLeft
u otra cosa no es realmente relevante.
Si obtienes los elementos en el mismo orden en que los usas, entonces es más probable que un ListBuffer
sea una opción preferible, si el rendimiento es crítico.
Pero, si no está en una ruta crítica y la entrada es lo suficientemente baja, siempre puede reverse
la lista más tarde, o simplemente foldRight
, o reverse
la entrada, que es de tiempo lineal.
Lo que NO HACE es usar una List
y anexarla. Esto le proporcionará un rendimiento mucho peor que el de adelantarse y retroceder al final.
Nota: esta respuesta está escrita para una versión anterior de Scala.
Las clases de la colección Scala se rediseñarán a partir de Scala 2.8, así que prepárese para cambiar la forma de crear listas muy pronto.
¿Cuál es la forma de crear una lista compatible con versiones anteriores? No tengo idea ya que aún no he leído los 2.8 documentos.
Un documento PDF que describe los cambios propuestos de las clases de colección