假设我们有一个副作用“生成器”函数f: () => Option[T]
,它在重复调用时返回Some
,直到它将永远返回None
的未来点。 (例如,在EOF处生成null
的包装Java API可能会有这种行为。)
是否可以将此函数包装为TraversableOnce
或Iterator
,具有以下约束:
Stream
中不想要var
Iterator
对象上有一些有用的方法,但没有任何与我的用例完全匹配的方法。欢迎任何想法!
答案 0 :(得分:6)
这就是诀窍:
def wrap[T](f: () => Option[T]): Iterator[T] = {
Iterator.continually(f()).takeWhile(_.isDefined).flatten
}
REPL测试:
scala> :paste
// Entering paste mode (ctrl-D to finish)
var i = 0
def sideEffectingFunc(): Option[Int] = {
i += 1
if (i < 10) Some(i)
else None
}
// Exiting paste mode, now interpreting.
i: Int = 0
sideEffectingFunc: ()Option[Int]
scala> val it = wrap(sideEffectingFunc)
it: Iterator[Int] = non-empty iterator
scala> it.toList
res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
答案 1 :(得分:2)
有点正交,这种行为可以使用协程来实现。至少有一个Scala库可以启用协同程序,您可以在此处找到它:http://storm-enroute.com/coroutines/
以下是您为获得所需内容而编写的代码示例:
import org.coroutines._
def sideEffectingFunction = coroutine { () =>
val limit = new scala.util.Random().nextInt(10)
val seq = new scala.util.Random
var counter = 0 // mutable state is preserved between coroutine invocations
while (counter < limit) {
counter += 1
yieldval(seq.nextInt)
}
}
defined function sideEffectingFunction
@ val cr = call(sideEffectingFunction())
cr: Coroutine.Frame[Int, Unit] = Coroutine.Frame<depth: 1, live: true>
@ cr.resume
res31: Boolean = true
@ cr.value
res32: Int = 57369026
@ cr.resume
res33: Boolean = true
@ cr.value
res34: Int = -1226825365
@ cr.resume
res35: Boolean = true
@ cr.value
res36: Int = 1304491970
@ cr.resume
res37: Boolean = false
@ cr.value
java.lang.RuntimeException: Coroutine has no value, because it did not yield.
scala.sys.package$.error(package.scala:27)
org.coroutines.Coroutine$Frame$mcI$sp.value$mcI$sp(Coroutine.scala:130)
cmd38$.<init>(Main.scala:196)
cmd38$.<clinit>(Main.scala:-1)
或者,或者:
@ val cr = call(sideEffectingFunction())
cr: Coroutine.Frame[Int, Unit] = Coroutine.Frame<depth: 1, live: true>
@ while(cr.resume) println(cr.value)
-1888916682
1135466162
243385373
或者,根据上一个答案的精神:
@ val cr = call(sideEffectingFunction())
cr: Coroutine.Frame[Int, Unit] = Coroutine.Frame<depth: 1, live: true>
@ cr.resume
res60: Boolean = true
@ val iter = Iterator.continually(cr.value).takeWhile(_ => cr.resume)
iter: Iterator[Int] = non-empty iterator
@ iter.foreach(println)
1595200585
995591197
-433181225
220387254
201795229
754208294
-363238006
协程方法的优点是你可以在底层副作用函数的调用之间保持一个可变状态,所有这些都很好地隐藏在协同程序内部的外部世界中。协同程序也可以组合并相互调用。
当然,协同程序为您提供的功能远远超过让您的任务正常工作,因此为此添加它们可能会有些过分。但是,这是一个值得注意的方便技术。