2009-07-05 11 views
8

Tôi không hiểu tại sao các tác giả cho rằng Mã 9.1 từ "Lập trình trong Scala" sử dụng đóng. Trong chương 9, họ cho thấy làm thế nào để cấu trúc lại mã vào nhiều hình thức ít bị trùng lặp, từ mã gốc này:Câu hỏi về đóng cửa Scala (Từ "Lập trình trong Scala")

object FileMatcher { 
    private def filesHere = (new java.io.File(".")).listFiles 
    def filesEnding(query: String) = 
    for (file <- filesHere; if file.getName.endsWith(query)) 
     yield file 
    def filesContaining(query: String) = 
    for (file <- filesHere; if file.getName.contains(query)) 
     yield file 
    def filesRegex(query: String) = 
    for (file <- filesHere; if file.getName.matches(query)) 
     yield file 
} 

Đối với phiên bản thứ hai:

object FileMatcher { 
    private def filesHere = (new java.io.File(".")).listFiles 
    def filesMatching(query: String, 
    matcher: (String, String) => Boolean) = { 
     for (file <- filesHere; if matcher(file.getName, query)) 
     yield file 
    }  
    def filesEnding(query: String) = 
    filesMatching(query, _.endsWith(_)) 
    def filesContaining(query: String) = 
    filesMatching(query, _.contains(_)) 
    def filesRegex(query: String) = 
    filesMatching(query, _.matches(_)) 
} 

Mà họ nói rằng không có sử dụng đóng cửa đây. Bây giờ tôi hiểu cho đến thời điểm này. Tuy nhiên họ giới thiệu việc sử dụng đóng cửa Refactor thậm chí một số chi tiết, thể hiện trong Liệt kê 9.1:

object FileMatcher { 
    private def filesHere = (new java.io.File(".")).listFiles 
    private def filesMatching(matcher: String => Boolean) = 
    for (file <- filesHere; if matcher(file.getName)) 
     yield file 
    def filesEnding(query: String) = 
    filesMatching(_.endsWith(query)) 
    def filesContaining(query: String) = 
    filesMatching(_.contains(query)) 
    def filesRegex(query: String) = 
    filesMatching(_.matches(query)) 
} 

Bây giờ họ nói rằng truy vấn là một biến miễn phí nhưng tôi không thực sự hiểu tại sao họ lại nói như vậy? Vì "" truy vấn "" dường như được truyền từ phương thức hàng đầu xuống đến hàm kết hợp chuỗi một cách rõ ràng.

Trả lời

17

Hãy xem xét phần đóng bổ sung cổ điển từ What is a closure.

(define (add a) 
    (lambda (b) 
    (+ a b))) 

(define add3 (add 3)) 

(add3 4) returns 7 

Trong biểu thức lambda trên, afree variable, được định nghĩa trong các liên kết Wikipedia là:

một biến được đề cập trong một hàm đó không phải là một biến cục bộ hoặc một đối số của hàm đó. Giá trị tăng cao là biến miễn phí đã bị ràng buộc (đóng cửa) với một khoản đóng.

Coming back to

def filesEnding(query: String) = 
    filesMatching(_.endsWith(query)) 

Chức năng tiềm ẩn x => x.endsWith(query) là chức năng hạng nhất, mà được gán cho giá trị đầu tiên-lớp matcher, và _.endsWith() đóng trên query, tương tự như cách 3 đóng cửa lên a trong (add 3). (add3 4) tương đương được thực hiện bởi matcher(file.getName).

Chỉnh sửa: Phần khó hiểu là tính năng trong Scala được gọi là hàm ẩn danh cú pháp trình giữ chỗ. Bằng cách sử dụng _ thay cho người gửi hoặc tham số, Scala sẽ tự động tạo ra một hàm ẩn danh, mà chúng ta có thể xem xét nó như là biểu thức lambda.

Ví dụ,

_ + 1    creates  x => x + 1 
_ * _    creates  (x1, x2) => x1 * x2 
_.endsWith(query) creates  x => x.endsWith(query) 

Trong chức năng x => x.endsWith(query), query đáp ứng hai yêu cầu trở thành một biến miễn phí:

  1. query không phải là một biến địa phương được xác định trong phạm vi chức năng (không có biến cục bộ).
  2. query không phải là đối số của hàm (đối số duy nhất là x).
+1

Tôi hiểu chính xác rằng vì phương pháp "đối sánh" thu thập biến "truy vấn" do đó nó sử dụng đóng. – Ekkmanz

+2

Có, trong mã này "def filesEnding (truy vấn: String) = filesMatching (_. EndsWith (truy vấn))" có một lambda "_.endsWith (truy vấn)" mà khi desugared một chút trông giống như "{x => x .endsWith (truy vấn)} ". Trong ký hiệu schemey sẽ giống như "(lambda (x) (endwith x query))". Như bạn có thể thấy, trong truy vấn "lambda" là một biến miễn phí. Nó không bị ràng buộc như một đối số cũng như không cho phép trong lambda, vì vậy khi truy vấn được tạo thành được truy vấn được lấy từ môi trường có chứa, ví dụ: invocations của các phương thức như "filesEnding". –