我在Scala中寻找一个FIFO流,即提供
功能的东西该流应该是可关闭的,应该阻止访问下一个元素,直到添加了元素或关闭了流。
实际上我有点惊讶的是,集合库没有(似乎)包含这样的数据结构,因为它是IMO非常经典的。
我的问题:
1)我忽略了什么吗?是否已有提供此功能的课程?
2)好的,如果它没有包含在集合库中,那么它可能只是现有集合类的一个简单组合。但是,我试图找到这个简单的代码,但对于这样一个简单的问题,我的实现看起来仍然非常复杂。对于这样的FifoStream,是否有更简单的解决方案?
class FifoStream[T] extends Closeable {
val queue = new Queue[Option[T]]
lazy val stream = nextStreamElem
private def nextStreamElem: Stream[T] = next() match {
case Some(elem) => Stream.cons(elem, nextStreamElem)
case None => Stream.empty
}
/** Returns next element in the queue (may wait for it to be inserted). */
private def next() = {
queue.synchronized {
if (queue.isEmpty) queue.wait()
queue.dequeue()
}
}
/** Adds new elements to this stream. */
def enqueue(elems: T*) {
queue.synchronized {
queue.enqueue(elems.map{Some(_)}: _*)
queue.notify()
}
}
/** Closes this stream. */
def close() {
queue.synchronized {
queue.enqueue(None)
queue.notify()
}
}
}
感谢您的建议。我略微修改了paradigmatic的解决方案,以便toStream返回一个不可变的流(允许可重复读取),以便它符合我的需要。为了完整起见,这里是代码:
import collection.JavaConversions._
import java.util.concurrent.{LinkedBlockingQueue, BlockingQueue}
class FIFOStream[A]( private val queue: BlockingQueue[Option[A]] = new LinkedBlockingQueue[Option[A]]() ) {
lazy val toStream: Stream[A] = queue2stream
private def queue2stream: Stream[A] = queue take match {
case Some(a) => Stream cons ( a, queue2stream )
case None => Stream empty
}
def close() = queue add None
def enqueue( as: A* ) = queue addAll as.map( Some(_) )
}
答案 0 :(得分:15)
在Scala中,流是“功能迭代器”。人们期望它们是纯粹的(没有副作用)和不可改变的。在这种情况下,每次迭代流时都会修改队列(所以它不是纯粹的)。这可能会产生很多误解,因为迭代两次相同的流会产生两种不同的结果。
话虽这么说,你应该使用Java BlockingQueues,而不是滚动你自己的实现。它们被认为在安全性和性能方面得到了很好的实施。这是我能想到的最干净的代码(使用你的方法):
import java.util.concurrent.BlockingQueue
import scala.collection.JavaConversions._
class FIFOStream[A]( private val queue: BlockingQueue[Option[A]] ) {
def toStream: Stream[A] = queue take match {
case Some(a) => Stream cons ( a, toStream )
case None => Stream empty
}
def close() = queue add None
def enqueue( as: A* ) = queue addAll as.map( Some(_) )
}
object FIFOStream {
def apply[A]() = new LinkedBlockingQueue
}
答案 1 :(得分:2)
我假设您正在寻找类似java.util.concurrent.BlockingQueue的内容?
Akka实现了此接口BoundedBlockingQueue。当然有java.util.concurrent中提供的实现。
你也可以考虑使用Akka的actors来做你正在做的事情。使用Actors通知或推送新事件或消息而不是拉动。
答案 2 :(得分:0)
1)看起来你正在寻找像Oz这样的语言看到的数据流,它支持生产者 - 消费者模式。这样的集合在集合API中不可用,但您可以自己创建一个集合。
2)数据流依赖于single-assignment variables的概念(这样它们不必在声明点初始化并在初始化之前读取它们导致阻塞):
val x: Int
startThread {
println(x)
}
println("The other thread waits for the x to be assigned")
x = 1
如果语言支持单一赋值(或数据流)变量,那么实现这样的流将是直截了当的(参见link)。由于它们不属于Scala,因此您必须像使用wait
- synchronized
- notify
模式一样使用。
Concurrent queues from Java也可以用来实现,就像其他用户建议的那样。