scala - 生成器/ block 到迭代器/流的转换

标签 scala callback yield continuations generator

基本上我想转换这个:

def data(block: T => Unit)

到 Stream (dataToStream 是进行此转换的假设函数):
val dataStream: Stream[T] = dataToStream(data)

我想这个问题可以通过延续来解决:
// let's assume that we don't know how data is implemented
// we just know that it generates integers
def data(block: Int => Unit) { for (i <- 0 to 10) block(i) }

// here we can print all data integers
data { i => println(i) }

// >> but what we really want is to convert data to the stream <<

// very dumb solution is to collect all data into a list
var dataList = List[Int]()
data { i => dataList = i::dataList }
// and make a stream from it
dataList.toStream

// but we want to make a lazy, CPU and memory efficient stream or iterator from data
val dataStream: Stream[Int] = dataToStream(data)
dataStream.foreach { i => println(i) }

// and here a black magic of continuations must be used
// for me this magic is too hard to understand
// Does anybody know how dataToStream function could look like?

谢谢,大卫

最佳答案

编辑:修改示例以显示 traversable.view 的懒惰

scala> def data(f : Int => Unit) = for(i <- 1 to 10) {    
     |   println("Generating " + i)
     |   f(i)
     | }
data: (f: (Int) => Unit)Unit

scala> def toTraversable[T]( func : (T => Unit) => Unit) = new Traversable[T] {
     |   def foreach[X]( f : T => X) = func(f(_) : Unit)                       
     | }                                                                       
toTraversable: [T](func: ((T) => Unit) => Unit)java.lang.Object with Traversable[T]

toTraversable 方法会将您的数据函数转换为 Traversable 集合。就其本身而言,它没什么大不了的,但是您可以将其转换为惰性的 TraversableView。下面是一个例子:
scala> toTraversable(data).view.take(3).sum
Generating 1
Generating 2
Generating 3
Generating 4
res1: Int = 6

take 方法的不幸本质是它必须超过生成的最后一个值才能正常工作,但它会提前终止。如果没有“.view”调用,上面的代码看起来是一样的。然而,这里有一个更引人注目的例子:
scala> toTraversable(data).view.take(2).foreach(println)
Generating 1
1
Generating 2
2
Generating 3

所以总而言之,我相信您正在寻找的集合是 TraversableView,它最容易创建 View ,使 Traversable 然后在其上调用“ View ”。如果你真的想要 Stream 类型,这里有一个在 2.8.0.final 中工作的方法,它将创建一个没有线程的“Stream”:
scala> def dataToStream( data : (Int => Unit) => Unit) = {
     |   val x = new Traversable[Int] {                     
     |     def foreach[U](f : Int => U) = {                 
     |        data( f(_) : Unit)                            
     |     }
     |   }
     |   x.view.toList.toStream                             
     | }
dataToStream: (data: ((Int) => Unit) => Unit)scala.collection.immutable.Stream[Int]

scala> dataToStream(data)
res8: scala.collection.immutable.Stream[Int] = Stream(0, ?)

这种方法的不幸之处在于,它会在创建流之前遍历整个可遍历对象。这也意味着所有值都需要缓存在内存中。唯一的选择是求助于线程。

顺便说一句:这是喜欢 Traversables 作为从 scalax.io.File 方法直接返回的动机:“行”、“字符”和“字节”。

关于scala - 生成器/ block 到迭代器/流的转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3797699/

相关文章:

scala - 使用惰性 val 缓存字符串表示

java - 在使用 Java API 处理 HDFS 时,我们是否需要显式调用 fs.close()?

javascript - 将 then 方法添加到回调函数

javascript - 在 jQuery 插件/Javascript 中产生一个变量

javascript - 中断循环的 WebWorker

java - 如何在 Scala 中反转义字符串?

scala - Scala 中的 mixins 错误

callback - 戈朗 : evaluate variable in callback declaration

javascript - Node.js:为什么在错误处理期间应该返回回调结果?

python - 在实践中,Python 3.3 中新的 "yield from"语法的主要用途是什么?