Đây là giải pháp sử dụng macros
. Cách tiếp cận chung ở đây là làm phong phú thêm Boolean
sao cho nó có phương thức macro
xem xét prefix
của ngữ cảnh để tìm so sánh được sử dụng để tạo ra Boolean
đó.
Ví dụ, giả sử chúng ta có:
implicit class RichBooleanComparison(val x: Boolean) extends AnyVal {
def <(rightConstant: Int): Boolean = macro Compare.ltImpl
}
Và một định nghĩa macro
với tiêu đề phương pháp:
def ltImpl(c: Context)(rightConstant: c.Expr[Int]): c.Expr[Boolean]
Bây giờ giả sử rằng trình biên dịch được phân tích cú pháp biểu thức 1 < 2 < 3
. Chúng tôi có thể sử dụng rõ ràng c.prefix
để nhận biểu thức 1 < 2
trong khi đánh giá nội dung phương thức macro. Tuy nhiên, khái niệm về constant folding ngăn chúng tôi làm như vậy tại đây. Hằng số không đổi là quá trình mà trình biên dịch tính toán các hằng số được xác định trước tại thời gian biên dịch. Vì vậy, theo các macro thời gian đang được đánh giá, c.prefix
đã được xếp thành chỉ true
trong trường hợp này. Chúng tôi đã mất biểu thức 1 < 2
dẫn đến true
. Bạn có thể đọc thêm về xếp liên tục và tương tác của chúng với macro Scala trên this issue và một chút trên this question.
Nếu chúng ta có thể giới hạn phạm vi của các cuộc thảo luận để chỉ biểu hiện dưới hình thức C1 < x < C2
, nơi C1
và C2
là hằng số, và x
là một biến, thì điều này trở nên khả thi, vì loại này biểu hiện sẽ không bị ảnh hưởng bởi liên tục gấp. Đây là một thực hiện:
object Compare {
def ltImpl(c: Context)(rightConstant: c.Expr[Int]): c.Expr[Boolean] = {
import c.universe._
c.prefix.tree match {
case Apply(_, Apply(Select([email protected](Constant(_)), _), ([email protected](_, TermName(_))) :: Nil) :: Nil) =>
val leftConstant = c.Expr[Int](lhs)
val variable = c.Expr[Int](x)
reify((leftConstant.splice < variable.splice) && (variable.splice < rightConstant.splice))
case _ => c.abort(c.enclosingPosition, s"Invalid format. Must have format c1<x<c2, where c1 and c2 are constants, and x is variable.")
}
}
}
Ở đây, chúng tôi phù hợp với bối cảnh prefix
với loại dự kiến, trích xuất các bộ phận liên quan (lhs
và x
), xây dựng subtrees mới sử dụng c.Expr[Int]
, và xây dựng một cây đầy đủ biểu hiện mới sử dụng reify
và splice
để thực hiện so sánh 3 chiều mong muốn. Nếu không phù hợp với loại dự kiến, điều này sẽ không biên dịch được.
Điều này cho phép chúng ta làm:
val x = 5
1 < x < 5 //true
6 < x < 7 //false
3 < x < 4 //false
Như mong muốn!
docs about macros, trees và this presentation là tài nguyên tốt để tìm hiểu thêm về macro.
'lớp ngầm' cho 'Boolean' với phương thức' <(y: Int) ', là macro và kiểm tra' tiền tố' của nó, giải mã nó thành 'x.y (a)' và ở đó bạn đi. – sjrd
@sjrd Tôi không nghĩ rằng đó là sẽ làm việc trong trường hợp chung vì gấp. –
Việc xếp chỉ xảy ra nếu 'a' là' giá trị cuối cùng 'không có loại. Trong ví dụ trên, tôi có thể đảm bảo rằng trình biên dịch không gấp nó (ít nhất là không trong quá trình typer, trước khi macro khởi động). – sjrd