2012-09-18 28 views
10

Trong tình huống sau đâyScala đuôi-đệ quy chức năng xử lý Suối quy định tại đặc điểm giữ tham chiếu đến dòng đầu

trait T { 

@tailrec 
def consume[A](as: Stream[A]): Unit = { 
    if (as.isEmpty)() 
    else consume(as.tail) 
    } 
} 

object O extends T 

gọi O.consume(Range(1, N).toStream) với N đủ lớn, chương trình sẽ chạy ra khỏi bộ nhớ, hoặc ít nhất là sẽ tiêu thụ O (N) thay vì O (1) cần thiết.

+0

Xem thêm http://stackoverflow.com/questions/12529697/how-to-write-non-leaking-tail-recursive-stream-function-in-scala – ron

Trả lời

10

Phương pháp đệ quy đuôi được tạo cho đặc điểm. Mục nhập phương thức trong phần mở rộng của đặc điểm (ở đây O) chuyển tiếp cuộc gọi đến phương thức của đặc điểm, nhưng trong khi thực hiện, nó giữ tham chiếu đến phần đầu của Luồng.

Vì vậy, phương pháp này là đệ quy đuôi, nhưng bộ nhớ vẫn không thể được giải phóng. Biện pháp khắc phục: Không xác định các hàm Luồng trong các đặc điểm, chỉ trực tiếp trong các đối tượng.

Một giải pháp thay thế là số EphemeralStream của scalaz, giữ tham chiếu yếu cho đầu và đuôi của luồng và tính toán lại theo yêu cầu.

+0

[EphemeralStream] (https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/EphemeralStream.scala) –

2

Có cách giải quyết đơn giản. Chỉ cần quấn đuôi dòng đệ quy tiêu dùng của bạn trong một chức năng tiếp nhận luồng qua một bởi tên tham số:

import scala.annotation.tailrec 

trait T { 
    def consume[A](as: => Stream[A]): Unit = { 
    @tailrec 
    def loop[A](as: Stream[A]): Unit = { 
     if (as.isEmpty)() 
     else loop(as.tail) 
    } 
    loop(as) 
    } 
} 

object O extends T { 
    def main(args: Array[String]): Unit = 
    O.consume(Range(1, 1000000000).toStream) 
} 

Phương pháp giao nhận sẽ tổ chức một tham chiếu đến một chức năng tính toán một biểu thức kết quả trong số đó là một dòng :

public final class O$ implements T { 
    public static final MODULE$; 
    // This is the forwarder: 
    public <A> void consume(Function0<Stream<A>> as) { 
    T.class.consume(this, as); 
    } 
    . . .  

    public void main(String[] args) { 
    consume(new AbstractFunction0() { 
     public final Stream<Object> apply() { 
     return package..MODULE$.Range().apply(1, 1000000000).toStream(); 
     } 
    }); 
    } 
} 
Các vấn đề liên quan