2012-01-19 13 views
10

Tôi mới sử dụng Scala và cố gắng quấn đầu quanh các lần tiếp tục Tôi đang cố gắng tạo lại tuyên bố C# yield return C#.Tiếp tục và để hiểu - tính không tương thích là gì?

Sau this post, tôi đã viết đoạn mã sau:

package com.company.scalatest 

import scala.util.continuations._; 

object GenTest { 

    val gen = new Generator[Int] { 
    def produce = { 
     yieldValue(1) 
     yieldValue(2) 
     yieldValue(3) 
     yieldValue(42) 
    } 
    } 
    // Does not compile :(

    // val gen2 = new Generator[Int] { 
    // def produce = { 
    //  var ints = List(1, 2, 3, 42); 
    // 
    //  ints.foreach((theInt) => yieldValue(theInt)); 
    // } 
    // } 

    // But this works? 
    val gen3 = new Generator[Int] { 
    def produce = { 
     var ints = List(1, 2, 3, 42); 
     var i = 0; 
     while (i < ints.length) { 
     yieldValue(ints(i)); 
     i = i + 1; 
     } 
    } 
    } 

    def main(args: Array[String]): Unit = { 
    gen.foreach(println); 
    // gen2.foreach(println); 
    gen3.foreach(println); 
    } 
} 

abstract class Generator[E] { 

    var loopFn: (E => Unit) = null 

    def produce(): Unit @cps[Unit] 

    def foreach(f: => (E => Unit)): Unit = { 
    loopFn = f 
    reset[Unit, Unit](produce) 
    } 

    def yieldValue(value: E) = 
    shift { genK: (Unit => Unit) => 
     loopFn(value) 
     genK(()) 
    () 
    } 
} 

Như bạn thấy, gen2 là nhận xét ra vì nó không biên dịch. Vì tôi có thể dễ dàng lặp qua nội dung của danh sách bằng cách sử dụng vòng lặp while (xem gen3), tôi dự kiến ​​vòng lặp foreach cũng sẽ hoạt động.

Các lỗi biên dịch như sau:

no type parameters for method foreach: (f: Int => B)Unit exist so that 
it can be applied to arguments (Int => Unit @scala.util.continuations.cpsParam[Unit,Unit]) 
--- because --- 
argument expression's type is not compatible with formal parameter type; 
found : Int => Unit @scala.util.continuations.cpsParam[Unit,Unit] 
required: Int => ?B 

Tại sao tôi nhận được lỗi này và có cách nào để làm việc xung quanh này với một cái gì đó sạch sẽ hơn một vòng lặp while?

Cảm ơn bạn

+2

bản sao có thể có của [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] (http: // stackoverflow.com/questions/2201882/implementation-yield-yield-return-use-yield-yield-return-using-scala-continuations) –

+0

Tôi không tìm câu trả lời cho tôi biết cách tạo lại 'lợi tức' bằng cách sử dụng tiếp tục Scala. Tôi đang tìm kiếm lý do tại sao 'gen2' trong ví dụ của tôi không hoạt động. 'Lợi tức lợi nhuận' chỉ là bối cảnh mà tôi gặp phải vấn đề này. – GuiSim

+0

Ah, xin lỗi sau đó. –

Trả lời

4

Đầu tiên chúng ta hãy nhìn vào những gì nó sẽ làm để có được gen2 để biên dịch.

object CpsConversions { 

    import scala.collection.IterableLike 
    import scala.util.continuations._ 

    implicit def cpsIterable[A, Repr](xs: IterableLike[A, Repr]) = new { 
    def cps = new { 
     def foreach[B](f: A => [email protected][Unit, Unit]): [email protected][Unit, Unit] = { 
     val it = xs.iterator 
     while(it.hasNext) f(it.next) 
     } 
    } 
    } 
} 

object GenTest { 

    import CpsConversions.cpsIterable 
    val gen2 = new Generator[Int] { 
    def produce = { 
     var ints = List(1, 2, 3, 42) 
     ints.cps.foreach((theInt) => yieldValue(theInt)) 
    } 
    } 

Bây giờ, hãy xem những gì đang diễn ra. Bản gốc gen2 thất bại trong việc biên dịch trên dòng sau:

ints.foreach((theInt) => yieldValue(theInt)) 

Kể từ khi loại yieldValue bao gồm chú thích @cpsParam, plugin continuations biến đổi chức năng truyền cho phương thức foreach đến một trong những loại:

Int => Unit @cpsParam[Unit,Unit] 

Đường lên trong hệ thống phân cấp của List[Int], bạn sẽ thấy foreach được định nghĩa là:

foreach [U] (f: (Int) ⇒ U): Unit 

Đây là vấn đề, vì các loại không khớp và Scala không biết cách nhận từ Int => U đến Int => Unit @cpsParam[Unit,Unit]. Để khắc phục sự cố, tôi đã thêm phiên bản CPS của foreach vào một chuyển đổi tiềm ẩn mà bạn có thể truy cập bằng cách gọi cps trên bất kỳ IterableLike nào. Nó sẽ rất tốt đẹp nếu chuyển đổi ngầm này có thể được thực hiện mà không có cuộc gọi rõ ràng cps, nhưng tôi đã không tìm thấy một cách để làm cho trình biên dịch Scala nhận ra khả năng áp dụng của một chuyển đổi tiềm ẩn để pimp mới foreach vào danh sách của bạn . Điều này có thể phải làm với thứ tự mà trình biên dịch sử dụng plugin liên tục, nhưng tôi biết quá ít về quá trình này để chắc chắn.

Vì vậy, đó là tất cả tốt và tốt cho foreach. Câu hỏi của bạn đề cập đến sự hiểu biết, yêu cầu bất kỳ số nào trong số filter, map hoặc flatMap phải được xác định (tùy thuộc vào những gì diễn ra trong hiểu của bạn). Tôi đã thực hiện chúng trong liên kết trong bình luận ở trên của tôi, mở rộng đối tượng CpsConversions ở trên để cho phép nói chung cho việc hiểu.