2014-12-10 17 views
5

Mã nàyXây dựng một biểu thức lambda sử dụng một dấu gạch dưới

(1 to 30).foreach { x => 
    println(x) 
    println 
} 

làm những gì tôi mong đợi: nó in mỗi 1-30, xen kẽ với khoảng trống. Tôi khá rõ ràng về những gì đang xảy ra ở đây, tôi nghĩ: Tôi đang chuyển một hàm ẩn danh mà lần đầu tiên in đối số của nó, và sau đó in một dòng trống.

Những gì tôi không hiểu là tại sao điều này không làm như vậy:

(1 to 30).foreach { 
    println _ 
    println 
} 

Có vẻ tương đương với tôi. Dấu gạch dưới nên đại diện cho đối số đầu tiên và duy nhất cho hàm; và hàm in đối số của nó và sau đó in một dòng trống. Nhưng khi tôi chạy phiên bản thứ hai này, tôi không nhận được những dòng trống.

Điều gì gây ra sự khác biệt này?

Trả lời

5

Biến thể đầu tiên là đơn giản:

  1. Trong dòng đầu tiên, áp dụng println trên x.
  2. Trong dòng thứ hai, áp dụng đối số println (này sẽ in dòng mới bổ sung).

Với biến thể thứ hai bạn có hiệu quả nói Scala để làm điều này:

  1. Trong dòng đầu tiên, xác định một đối tượng hàm từ println(). Sau đó, không làm gì với đối tượng mới được tạo này.
  2. Trong dòng thứ hai, áp dụng println cho đối số (phần tử của chuỗi ).

Sự nhầm lẫn xuất phát từ giả định rằng println(x)println _ là tương đương. Chúng khác nhau. Cú pháp funcId _ xác định một hàm mới dựa trên funcId, nó không giống như sử dụng ký pháp "đối số gạch dưới" khi gọi hàm.

3

Có một số điều đang diễn ra tại đây.

Đầu tiên, tất cả cú pháp trình giữ chỗ tham số chỉ có thể được sử dụng trong các dấu ngoặc đơn bên ngoài của định nghĩa lambda. Nó không thể được sử dụng trong các dấu ngoặc đơn của phương thức mà bạn thực hiện trong định nghĩa lambda.

Dưới đây là ví dụ để minh họa điểm này.

val a = (1 to 10).map(_ + 1) 

Điều này sẽ hiệu quả.

val b = (1 to 10).map(math.sin(_ + 1)) 

Điều này sẽ không hoạt động.

Do đó, mã của bạn không sử dụng cú pháp trình giữ chỗ tham số. Thay vào đó, nó sử dụng các hàm được áp dụng một phần.

Ví dụ

(1 to 10).foreach(println _) 

là chức năng tương đương với

val a = println (_ : Int) 
(1 to 10).foreach(a) 

Ngoài ra khi một tên phương pháp được sử dụng trong biểu thức lambda dấu gạch chân có thể được bỏ qua. Scala sẽ vẫn tạo ra phương thức được áp dụng một phần.

Do đó

(1 to 10).foreach(println) 

bằng

(1 to 10).foreach(println _) 

Và do đó mã của bạn bằng

val a = println (_ : Int) 
    (1 to 10).foreach{ 
    a 
    a 
    } 

Và vì {aa} trả về một, nó tương đương với

val a = println (_ : Int) 
(1 to 10).foreach(a) 
0

Để thêm vào câu trả lời khác, có thực sự tồn tại một cách để sử dụng println(_) và không phải khai báo x tham số:

(1 to 30).foreach { 
    (println(_: Int)) 
    .andThen(_ => println) 
} 

Đây foreach tham số là chức năng, mà trước hết là gọi println(_) cho các phần tử phạm vi, và sau đó đi println(Int) kết quả (là (): Unit) với một hàm khác, _ => println, bỏ qua đối số của nó và in dòng mới.

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