تنفيذ العائد (عودة العائد) باستخدام مستمرات SCALA
-
18-09-2019 - |
سؤال
كيف يمكن أن ينفذ واحد C # yield return
باستخدام مستمرات scala؟ أود أن أكون قادرا على كتابة Scala Iterator
S في نفس النمط. طعنة في التعليقات على هذا آخر الأخبار scala, ، لكنه لا يعمل (حاول استخدام بيتا Scala 2.8.0). إجابات في سؤال ذي صلة أقترح أن هذا ممكن، ولكن على الرغم من أنني كنت ألعب مع استمرار محددة لفترة من الوقت، إلا أنني لا أستطيع أن ألتفها بالضبط رأسي حول كيفية القيام بذلك.
المحلول
قبل أن نقدم استمرارنا، نحتاج إلى بناء بعض البنية التحتية. أدناه هو الترامبولين التي تعمل على Iteration
أشياء. التكرار هو حساب يمكن إما Yield
قيمة جديدة أو يمكن أن يكون Done
.
sealed trait Iteration[+R]
case class Yield[+R](result: R, next: () => Iteration[R]) extends Iteration[R]
case object Done extends Iteration[Nothing]
def trampoline[R](body: => Iteration[R]): Iterator[R] = {
def loop(thunk: () => Iteration[R]): Stream[R] = {
thunk.apply match {
case Yield(result, next) => Stream.cons(result, loop(next))
case Done => Stream.empty
}
}
loop(() => body).iterator
}
يستخدم الترامبولين حلقة داخلية تقوم بتحويل تسلسل Iteration
الكائنات في أ Stream
وبعد ثم نحصل على Iterator
بالاتصال iterator
على كائن دفق الناتج. باستخدام أ Stream
تقييمنا كسول. نحن لا نقيم التكرار الخاص بنا حتى هناك حاجة.
يمكن استخدام الترامبولين لبناء جهاز استئناف مباشر.
val itr1 = trampoline {
Yield(1, () => Yield(2, () => Yield(3, () => Done)))
}
for (i <- itr1) { println(i) }
هذا فظيع جدا للكتابة، لذلك دعونا نستخدم مستمرات محددة لإنشاء لدينا Iteration
الكائنات تلقائيا.
نحن نستخدم ال shift
و reset
المشغلين لكسر الحساب Iteration
S، ثم استخدام trampoline
لتحويل Iteration
S في Iterator
.
import scala.continuations._
import scala.continuations.ControlContext.{shift,reset}
def iterator[R](body: => Unit @cps[Iteration[R],Iteration[R]]): Iterator[R] =
trampoline {
reset[Iteration[R],Iteration[R]] { body ; Done }
}
def yld[R](result: R): Unit @cps[Iteration[R],Iteration[R]] =
shift((k: Unit => Iteration[R]) => Yield(result, () => k(())))
الآن يمكننا إعادة كتابة مثالنا.
val itr2 = iterator[Int] {
yld(1)
yld(2)
yld(3)
}
for (i <- itr2) { println(i) }
أفضل بكثير!
الآن هنا مثال من صفحة # المرجعية بالنسبة yield
هذا يظهر بعض الاستخدام الأكثر تقدما. يمكن أن تكون الأنواع صعبة بعض الشيء للتعود عليها، ولكن كل ذلك يعمل.
def power(number: Int, exponent: Int): Iterator[Int] = iterator[Int] {
def loop(result: Int, counter: Int): Unit @cps[Iteration[Int],Iteration[Int]] = {
if (counter < exponent) {
yld(result)
loop(result * number, counter + 1)
}
}
loop(number, 0)
}
for (i <- power(2, 8)) { println(i) }
نصائح أخرى
تمكنت من اكتشاف طريقة للقيام بذلك، بعد بضع ساعات أخرى من اللعب حولها. اعتقدت أن هذا كان أبسط لالتفاف رأسي حول جميع الحلول الأخرى التي رأيتها حتى الآن، على الرغم من أنني فعلت ذلك بعد ذلك كثيرا اميال' حلول.
def loopWhile(cond: =>Boolean)(body: =>(Unit @suspendable)): Unit @suspendable = {
if (cond) {
body
loopWhile(cond)(body)
}
}
class Gen {
var prodCont: Unit => Unit = { x: Unit => prod }
var nextVal = 0
def yld(i: Int) = shift { k: (Unit => Unit) => nextVal = i; prodCont = k }
def next = { prodCont(); nextVal }
def prod = {
reset {
// following is generator logic; can be refactored out generically
var i = 0
i += 1
yld(i)
i += 1
yld(i)
// scala continuations plugin can't handle while loops, so need own construct
loopWhile (true) {
i += 1
yld(i)
}
}
}
}
val it = new Gen
println(it.next)
println(it.next)
println(it.next)