2009-04-15 30 views
109

Cách đối sánh mẫu trong Scala được triển khai ở cấp độ bytecode như thế nào?Cách đối sánh mẫu trong Scala được triển khai ở cấp độ bytecode như thế nào?

Có phải nó giống như một loạt các cấu trúc if (x instanceof Foo) hoặc thứ gì khác không? Hiệu quả của nó là gì?

Ví dụ: được cung cấp mã sau (từ Scala By Example trang 46-48), mã Java tương đương cho phương thức eval trông như thế nào?

abstract class Expr 
case class Number(n: Int) extends Expr 
case class Sum(e1: Expr, e2: Expr) extends Expr 

def eval(e: Expr): Int = e match { 
    case Number(x) => x 
    case Sum(l, r) => eval(l) + eval(r) 
} 

P.S. Tôi có thể đọc Java bytecode, do đó, một biểu diễn bytecode sẽ đủ tốt cho tôi, nhưng có lẽ nó sẽ tốt hơn cho các độc giả khác để biết nó trông giống như mã Java như thế nào.

P.P.S. Cuốn sách Programming in Scala có trả lời cho câu hỏi này và các câu hỏi tương tự về cách Scala được triển khai không? Tôi đã ra lệnh cho cuốn sách, nhưng nó vẫn chưa đến.

+0

Tại sao bạn không biên dịch ví dụ và tháo rời nó bằng trình tách rời bytecode của Java? – Zifre

+0

Tôi có thể sẽ làm điều đó, trừ khi ai đó đưa ra câu trả lời hay trước. Nhưng bây giờ tôi muốn ngủ một chút. ;) –

+22

Câu hỏi này rất hữu ích cho người đọc khác! – djondal

Trả lời

85

Mức thấp có thể được khám phá với một disassembler nhưng câu trả lời ngắn gọn là rằng đó là một bó nếu/elses nơi vị phụ thuộc vào mô hình

case Sum(l,r) // instance of check followed by fetching the two arguments and assigning to two variables l and r but see below about custom extractors 
case "hello" // equality check 
case _ : Foo // instance of check 
case x => // assignment to a fresh variable 
case _ => // do nothing, this is the tail else on the if/else 

Có nhiều hơn nữa mà bạn có thể làm với các mẫu như hoặc các mẫu và kết hợp như "trường hợp Foo (45, x)", nhưng nói chung chúng chỉ là những phần mở rộng hợp lý của những gì tôi vừa mô tả. Các mẫu cũng có thể có các bảo vệ, là những ràng buộc bổ sung đối với các biến vị ngữ. Cũng có những trường hợp trình biên dịch có thể tối ưu hóa kết hợp mẫu, ví dụ: khi có sự chồng chéo giữa các trường hợp, nó có thể kết hợp mọi thứ một chút. Các mẫu và tối ưu hóa nâng cao là một khu vực hoạt động trong trình biên dịch, vì vậy đừng ngạc nhiên nếu mã byte cải thiện đáng kể so với các quy tắc cơ bản này trong các phiên bản hiện tại và tương lai của Scala.

Ngoài tất cả những điều đó, bạn có thể viết trình giải nén tùy chỉnh của riêng bạn ngoài hoặc thay thế các trình gỡ bỏ mặc định mà Scala sử dụng cho các lớp chữ hoa chữ thường. Nếu bạn làm vậy, thì chi phí của khớp mẫu là chi phí của bất kỳ bộ chiết nào. Tổng quan tốt được tìm thấy trong http://lamp.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf

70

James (ở trên) đã nói rõ nhất. Tuy nhiên, nếu bạn tò mò nó luôn luôn là một bài tập tốt để nhìn vào bytecode tháo rời. Bạn cũng có thể gọi scalac bằng tùy chọn -print, tùy chọn này sẽ in chương trình của bạn với tất cả các tính năng cụ thể của Scala đã bị xóa. Về cơ bản nó là Java trong trang phục của Scala. Đây là scalac -print đầu ra phù hợp cho đoạn mã bạn đã cho:

def eval(e: Expr): Int = { 
    <synthetic> val temp10: Expr = e; 
    if (temp10.$isInstanceOf[Number]()) 
    temp10.$asInstanceOf[Number]().n() 
    else 
    if (temp10.$isInstanceOf[Sum]()) 
     { 
     <synthetic> val temp13: Sum = temp10.$asInstanceOf[Sum](); 
     Main.this.eval(temp13.e1()).+(Main.this.eval(temp13.e2())) 
     } 
    else 
     throw new MatchError(temp10) 
}; 
28

Kể từ phiên bản 2.8, Scala đã có @switch chú thích. Mục đích là để đảm bảo, kết hợp mẫu đó sẽ được biên dịch thành tableswitch or lookupswitch thay vì hàng loạt câu lệnh có điều kiện if.

+5

khi nào để chọn @switch trên thường xuyên nếu khác? –

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