2010-02-04 38 views
27

Làm cách nào để triển khai C# yield return bằng cách sử dụng tiếp tục Scala? Tôi muốn có thể viết Scala Iterator s theo cùng một kiểu. Một đâm là trong các ý kiến ​​trên this Scala news post, nhưng nó không hoạt động (đã thử sử dụng bản beta Scala 2.8.0). Câu trả lời trong một related question cho thấy điều này là có thể, nhưng mặc dù tôi đã chơi với các liên tục giới hạn trong một thời gian, tôi không thể dường như chính xác quấn quanh đầu của tôi như thế nào để làm điều này.Thực hiện lợi nhuận (lợi tức lợi nhuận) bằng cách sử dụng tiếp tục Scala

+0

Điều gì không phù hợp với ví dụ đó? Nó không biên dịch, hoặc nó không tạo ra kết quả mong đợi? Có một đề cập rằng, để nó hoạt động, nó có thể cần thiết để có một 'foreach' nhận thức CPS, nhưng, ở mức nào, nó sẽ rất hữu ích khi biết vấn đề là gì. –

+0

Nó không biên dịch. – Yang

+2

Bạn có thể muốn kiểm tra câu trả lời của Miles Sabin cho một câu hỏi tương tự mà tôi có http://stackoverflow.com/questions/2137619/scala-equivalent-to-python-generators/2146456#2146456. Không chắc chắn nó giúp bạn gần gũi hơn. – huynhjl

Trả lời

41

Trước khi chúng tôi giới thiệu phần tiếp theo, chúng tôi cần xây dựng một số cơ sở hạ tầng. Dưới đây là trampoline hoạt động trên Iteration đối tượng. Lặp lại là phép tính có thể là Yield giá trị mới hoặc có thể là 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 
} 

Các tấm bạt lò xo sử dụng một vòng lặp nội bộ có thể biến các chuỗi các Iteration đối tượng vào một Stream. Sau đó, chúng tôi nhận được Iterator bằng cách gọi iterator trên đối tượng luồng kết quả. Bằng cách sử dụng số Stream đánh giá của chúng tôi là lười biếng; chúng tôi không đánh giá lần lặp tiếp theo cho đến khi cần.

Tấm bạt lò xo có thể được sử dụng để xây dựng trình lặp trực tiếp.

val itr1 = trampoline { 
    Yield(1,() => Yield(2,() => Yield(3,() => Done))) 
} 

for (i <- itr1) { println(i) } 

Thật là khủng khiếp khi viết, vì vậy, hãy sử dụng liên tục được giới hạn để tự động tạo các đối tượng Iteration của chúng tôi.

Chúng tôi sử dụng các nhà khai thác shiftreset để phá vỡ tính toán thành Iteration s, sau đó sử dụng trampoline để biến Iteration s thành một 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(()))) 

Bây giờ chúng ta có thể viết lại ví dụ của mình.

val itr2 = iterator[Int] { 
    yld(1) 
    yld(2) 
    yld(3) 
} 

for (i <- itr2) { println(i) } 

Tốt hơn nhiều!

Bây giờ, đây là ví dụ từ C# reference page cho yield hiển thị một số cách sử dụng nâng cao hơn. Các loại có thể hơi phức tạp một chút để làm quen, nhưng tất cả đều hoạt động.

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) } 
+0

Tôi muốn xem đầu ra của scalac -print cho iterator, yld và gán cho itr2. Ai đó có thể thêm plugin này vào câu trả lời? – retronym

+0

Tôi chỉ cố gắng áp dụng điều này vì vậy tôi đã có mã chạy và tiện dụng. Xem http://gist.github.com/297230 cho đầu ra (cuộn xuống dưới). – huynhjl

+1

Tôi sẽ đổi tên 'iterator' thành' yldIterator' hoặc một cái gì đó tương tự, để tránh nhầm lẫn. :-) –

5

Tôi đã khám phá cách để thực hiện việc này sau vài giờ nữa. Tôi nghĩ rằng điều này đơn giản hơn để quấn đầu của tôi xung quanh hơn tất cả các giải pháp khác mà tôi đã nhìn thấy cho đến nay, mặc dù tôi đã làm sau đó rất nhiều đánh giá cao của Rich và Miles' giải pháp.

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) 
+2

Việc tiếp tục Scala không thể xử lý trong khi lặp lại? Ouch! –

+0

Thật vậy. : (Hy vọng rằng đó là một công việc đang tiến triển, nhưng tôi tin rằng sự hiểu biết chắc chắn không tương thích với sự thay đổi, vì điều đó có nghĩa là sẽ tách rời bản đồ/foreach/etc. – Yang

+3

Không còn nữa. Hãy gọi mã cps từ bên trong Đối với sự hiểu biết vẫn không được hỗ trợ mặc dù (Tôi không thực sự nghĩ rằng họ sẽ được hỗ trợ bao giờ) –

Các vấn đề liên quan