El problema, como han dicho otros, es que filter
crea una nueva colección. La alternativa withFilter
no, pero eso no tiene un foldLeft
. Además, usar .view
, .iterator
o .toStream
evitaría crear la nueva colección de varias maneras, pero aquí son más lentas que el primer método que usaste, lo cual me pareció algo extraño al principio.
Pero, entonces ...Ver, 1 until 1000
es un Range
, cuyo tamaño es realmente muy pequeño, porque no almacena cada elemento. Además, Range
's foreach
está extremadamente optimizado, e incluso es specialized
, que no es el caso de ninguna de las otras colecciones. Dado que foldLeft
se implementa como foreach
, siempre que se quede con un Range
podrá disfrutar de sus métodos optimizados.
(_: Range).foreach
:
@inline final override def foreach[@specialized(Unit) U](f: Int => U) {
if (length > 0) {
val last = this.last
var i = start
while (i != last) {
f(i)
i += step
}
f(i)
}
}
(_: Range).view.foreach
def foreach[U](f: A => U): Unit =
iterator.foreach(f)
(_: Range).view.iterator
override def iterator: Iterator[A] = new Elements(0, length)
protected class Elements(start: Int, end: Int) extends BufferedIterator[A] with Serializable {
private var i = start
def hasNext: Boolean = i < end
def next: A =
if (i < end) {
val x = self(i)
i += 1
x
} else Iterator.empty.next
def head =
if (i < end) self(i) else Iterator.empty.next
/** $super
* '''Note:''' `drop` is overridden to enable fast searching in the middle of indexed sequences.
*/
override def drop(n: Int): Iterator[A] =
if (n > 0) new Elements(i + n, end) else this
/** $super
* '''Note:''' `take` is overridden to be symmetric to `drop`.
*/
override def take(n: Int): Iterator[A] =
if (n <= 0) Iterator.empty.buffered
else if (i + n < end) new Elements(i, i + n)
else this
}
(_: Range).view.iterator.foreach
def foreach[U](f: A => U) { while (hasNext) f(next()) }
Y eso, por supuesto, ni siquiera las cuenten la filter
entre view
y foldLeft
:
override def filter(p: A => Boolean): This = newFiltered(p).asInstanceOf[This]
protected def newFiltered(p: A => Boolean): Transformed[A] = new Filtered { val pred = p }
trait Filtered extends Transformed[A] {
protected[this] val pred: A => Boolean
override def foreach[U](f: A => U) {
for (x <- self)
if (pred(x)) f(x)
}
override def stringPrefix = self.stringPrefix+"F"
}
¿Cómo se midió que el código es "órdenes de magnitud más rápido"? En mi computadora portátil * extremadamente * vieja, * extremadamente * lenta, en el intérprete * de Scala *, la versión que usted afirma es "órdenes de magnitud más lenta" toma menos de 300 μs (eso es * micro * segundos), ¿cuánto tiempo dura * ¿versión rápida *? ¿Retrocede en el tiempo? La mayoría de las JVM de alto rendimiento necesitan aproximadamente 5 * segundos * solo para calentar sus cachés y esas cosas, incluso antes de que alcancen su velocidad máxima. (El compilador C2 en HotSpot JVM, que es el compilador de optimización, ni siquiera compila un método hasta que se haya ejecutado 20000 veces). –
La segunda versión parece ser aproximadamente 3 veces más rápida (por mi medición totalmente no científica usando '(1 a 10000000) 'en REPL). Yo no llamaría eso "órdenes de magnitud", pero aún así. –
@ Jörg: puede ver los tiempos de ejecución en su enlace ideone, pero editaré esa información en la pregunta para que no se pierda si el enlace ideone desaparece. –