Quiero mantener una cola FIFO delimitada inmutable desde la que puedo eliminar los valores más antiguos después de un cierto tiempo. En Scala, el inmutable.Queue funciona bien para de tamaño limitado colas (.size parece ser O (N) ya que internamente se basa en List, pero puedo mantener el tamaño por separado), pero parece que no hay forma económica para acceder al elemento principal para probar la edad del valor más antiguo con algo más barato que O (N), por lo que no puedo probar el estado de caducidad de la entrada más antigua. ¿Alguna sugerencia para una implementación puramente funcional (inmutable)?¿Hay una implementación puramente funcional para una cola limitada con peek() en O (1)?
Respuesta
En este artículo, Haskell: Queues without pointers, describe una cola puramente funcional con O (1) coste amortizado (editar: para añadir y eliminar elementos). Creo que la estructura de datos proviene de Chris Okasaki y hay más detalles en his book.
La idea básica es descomponer la cola en dos listas, una para el frente y otra para la parte posterior. Los nuevos elementos se agregan a "frente". "Atrás" se almacena en orden inverso, para facilitar la aparición de elementos. Cuando todos los elementos de "atrás" desaparecen, "frente" se invierte y se vuelve a identificar como "atrás". Esta estructura de datos tiene O (1) costo amortizado para estas operaciones, pero aparentemente con algún trabajo puede reducirse a O (1), propiamente dicha.
Editar: Okasaki's paper describe una implementación elegante y puramente funcional de colas y colas de dos extremos (deques). Deques permite agregar o eliminar elementos de cualquier extremo. Todas esas operaciones son O (1), el peor de los casos.
La norma immutable.Queue
en Scala se puede adaptar para funcionar así, para una complejidad amortizada. Sin embargo, tenga en cuenta que la operación peek
devolverá una nueva cola o, de lo contrario, las llamadas consecutivas a peek
bien podrían hacerse en O (n).
Puede ampliar Queue
o crear una clase totalmente nueva adaptándola. Aquí hay una versión extenderla:
import scala.collection._
import generic._
import immutable.Queue
import mutable.{ Builder, ListBuffer }
class MyQueue[+A] protected(in0: List[A], out0: List[A]) extends scala.collection.immutable.Queue[A](in0, out0) with GenericTraversableTemplate[A, MyQueue] with LinearSeqLike[A, MyQueue[A]] {
override def companion: GenericCompanion[MyQueue] = MyQueue
def peek: (A, MyQueue[A]) = out match {
case Nil if !in.isEmpty => val rev = in.reverse ; (rev.head, new MyQueue(Nil, rev))
case x :: xs => (x, this)
case _ => throw new NoSuchElementException("dequeue on empty queue")
}
override def tail: MyQueue[A] =
if (out.nonEmpty) new MyQueue(in, out.tail)
else if (in.nonEmpty) new MyQueue(Nil, in.reverse.tail)
else throw new NoSuchElementException("tail on empty queue")
override def enqueue[B >: A](elem: B) = new MyQueue(elem :: in, out)
// This ought to be override, but scalac doesn't think so!
def enqueue[B >: A](iter: Iterable[B]) =
new MyQueue(iter.toList.reverse ::: in, out)
override def dequeue: (A, MyQueue[A]) = out match {
case Nil if !in.isEmpty => val rev = in.reverse ; (rev.head, new MyQueue(Nil, rev.tail))
case x :: xs => (x, new MyQueue(in, xs))
case _ => throw new NoSuchElementException("dequeue on empty queue")
}
override def toString() = mkString("MyQueue(", ", ", ")")
}
object MyQueue extends SeqFactory[MyQueue] {
implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, MyQueue[A]] = new GenericCanBuildFrom[A]
def newBuilder[A]: Builder[A, MyQueue[A]] = new ListBuffer[A] mapResult (x => new MyQueue[A](Nil, x.toList))
override def empty[A]: MyQueue[A] = EmptyQueue.asInstanceOf[MyQueue[A]]
override def apply[A](xs: A*): MyQueue[A] = new MyQueue[A](Nil, xs.toList)
private object EmptyQueue extends MyQueue[Nothing](Nil, Nil) { }
}
Si entiendo bien la pregunta, que busca una cola de doble extremo (deque ). Hay documentos de Okasaki, Kaplan y Tarjan sobre deques puramente funcionales. En cuanto a las puestas en práctica, lo más fácil es que creo que tomar la implementación predeterminada de collection.immutable.IndexedSeq
que es collection.immutable.Vector
, según this table haber estimado los costos constantes para head
y last
(se dice tail
pero yo supongo que last
es también O (1)).
El Okasaki/Kaplan/Tarjan uno parece haber sido implementado por Henry Ware.
La otra implementación que viene a la mente es el FingerTree de Hintze, para el que existen varias implementaciones en scala. Scalaz tiene uno que hace algún tiempo puse en a separate package ya que lo uso mucho. De acuerdo con una presentación de Daniel Spiewak (no recuerdo dónde vi esto), el FingerTree es bastante lento en los factores de tiempo constante, y también la página de Henry Ware dice que es más lenta que su otra implementación.
- 1. Implementación de una cola basada en archivos
- 2. ¿Cuál es el algoritmo puramente funcional más eficiente para generar todos los prefijos de una lista?
- 3. ¿Hay una implementación para SqlGeometryBuilder?
- 4. ¿Hay alguna vez una razón lógica para usar 1 = 1 en una cláusula where?
- 5. ¿Hay una implementación de "getopt" para Delphi?
- 6. cola Funcional De Programación En Scala
- 7. Lista doblemente enlazada en un lenguaje de programación puramente funcional
- 8. ¿Hay una implementación R para Java o .NET?
- 9. ¿Cómo puedo implementar un montón binario estándar puramente funcional (ocaml o haskell)?
- 10. ¿Es posible implementar una versión js del descomprimido de Haskell de una manera puramente funcional?
- 11. ¿Hay una implementación 'multimap' en Python?
- 12. Implementación de una cola simple usando matrices
- 13. ¿Hay enlaces de lenguaje puramente funcionales disponibles para Selenium2/WebDriver?
- 14. Ordenando una cola usando la misma cola
- 15. ¿Hay una implementación de mapa con oyentes para Java?
- 16. ¿Hay una implementación C++ para árboles vEB?
- 17. ¿Hay una implementación C++ MinMax Heap?
- 18. ¿Hay una implementación HAML para usar con Python y Django
- 19. Implementación de cola en C#
- 20. script cron para actuar como una cola O una cola para cron?
- 21. localmente la edición de un árbol puramente funcional
- 22. ¿Hay una implementación null std :: ostream en C++ o bibliotecas?
- 23. ¿Existen esquemas o Lisps puramente funcionales?
- 24. ¿Cómo implemento una función Peek() en un DataReader?
- 25. ¿Haskell es realmente un lenguaje puramente funcional considerando UnpafePerformIO?
- 26. Implementación de una estructura de datos de diccionario funcional/persistente
- 27. ¿Por qué ArrayBlockingQueue se llama una cola limitada mientras que LinkedBlockingQueue se llama cola de bloqueo ilimitada?
- 28. implementación de cola de trabajos para python
- 29. Una cola rápida en Java
- 30. ¿Hay una cola de tamaño fijo que elimine elementos excesivos?
O (1) costo amortizado de _what_? No tiene sentido hablar de la complejidad de una operación sin decir cuál es esa operación. En este caso, la operación es dequerante, que es similar pero no es igual a mirar a escondidas. –
Es O (1) costo amortizado por agregar y eliminar elementos. Al mirar, ¿te refieres a ver el elemento en la parte superior de la cola sin quitarlo? Esto se implementa fácilmente como una operación O (1): por ejemplo, se puede agregar trivialmente un campo adicional a la estructura de datos que recuerda el elemento superior. Mantener el tamaño funciona de la misma manera, como lo menciona Alex. –
@Daniel, ahora me doy cuenta de que se refería a echar un vistazo al elemento al final de la cola. En mi respuesta, este es solo el encabezado de la lista "de nuevo", así que de nuevo O (1) vez. Si uno necesita echar un vistazo a múltiples valores al final de la cola, uno puede asegurarse de que la lista "atrás" sea siempre lo suficientemente grande. –