2010-07-10 31 views
7

Chỉ cần nhìn thấy một khả năng thú vị để khởi tạo các khối mã trong Scala cho các chức năng bậc cao như foreach hoặc bản đồ:scala foreach và bản đồ initializers

(1 to 3) map { 
    val t = 5 
    i => i * 5 
} 


(1 to 3) foreach { 
    val line = Console.readLine 
    i => println(line) 
} 

Đây có phải là một số tính năng ghi chép hay tôi nên tránh các cấu trúc như vậy? Tôi có thể tưởng tượng, khối "khởi tạo" đi vào constructor và bản thân đóng cửa trở thành phương thức apply()?

Cảm ơn Pat cho câu hỏi ban đầu (http://extrabright.com/blog/2010/07/10/scala-question-regarding-readline)

Trả lời

12

Trong khi các tính năng được sử dụng không phải là hiếm, tôi sẽ thừa nhận là một sự kết hợp khá kỳ lạ của các đối tượng địa lý. Bí quyết cơ bản là bất kỳ khối nào trong Scala là một biểu thức, với kiểu giống như biểu thức cuối cùng trong khối. Nếu biểu thức cuối cùng đó là một hàm, điều này có nghĩa là khối có kiểu chức năng, và do đó có thể được sử dụng làm đối số cho "bản đồ" hoặc "foreach". Điều xảy ra trong những trường hợp này là khi "map" hoặc "foreach" được gọi, khối được đánh giá. Khối đánh giá thành một hàm (i => i * 5 trong trường hợp đầu tiên), và hàm đó sau đó được ánh xạ trên phạm vi.

Có thể sử dụng cấu trúc này cho khối để xác định các biến có thể thay đổi và hàm kết quả sẽ thay đổi biến mỗi khi được gọi. Các biến sẽ được khởi tạo một lần, được đóng bởi hàm và giá trị của chúng được cập nhật mỗi lần hàm được gọi.

Ví dụ, đây là một cách hơi ngạc nhiên tính 6 số thừa đầu tiên

(1 to 6) map { 
     var total = 1 
     i => {total *= i;total} 
    } 

(BTW, xin lỗi cho việc sử dụng thừa làm ví dụ. Đó là một trong hai hoặc chức năng quy tắc progamming Guild fibonacci.. Bạn phải giải quyết vấn đề đó, mang theo các chàng trai xuống đại sảnh.)

Lý do ít quan trọng hơn để có một khối trả về một hàm là định nghĩa các hàm trợ giúp trước đó trong khối. Ví dụ, nếu ví dụ thứ hai của bạn là thay vì

(1 to 3) foreach { 
    def line = Console.readLine 
    i => println(line) 
} 

Kết quả sẽ là ba dòng được đọc và lặp lại một lần mỗi, trong khi ví dụ của bạn có dòng đọc một lần và lặp lại ba lần.

+0

Câu trả lời chính xác hơn nhiều so với tôi. +1 – VonC

+0

Trong ví dụ giai thừa, bạn nên sử dụng 'total * = i' thay vì giới thiệu một biến thứ hai có tên' counter' –

+0

Vâng, tôi nhận ra rằng sau này. Sẽ chỉnh sửa –

1

Thứ nhất, những nhận xét của blog gốc "Scala Question Regarding readLine" bài đề cập đến

Các “line” là một giá trị và không thể được thực hiện, đó là chỉ được gán một lần từ kết quả thực hiện phương thức “Console.readLine”.
Nó được sử dụng ít hơn ba lần trong đóng cửa của bạn.
Nhưng nếu bạn định nghĩa nó như là một phương pháp, nó sẽ được thực hiện ba lần:

(1 to 3) foreach { 
    def line = Console.readLine 
    i => println(line) 
} 

Blog Scala for Java Refugees Part 6: Getting Over Java có một phần thú vị về chức năng bậc cao, bao gồm:

Scala cung cấp vẫn linh hoạt hơn trong cú pháp cho những thứ chức năng bậc cao hơn này.
Trong lời gọi lặp lại, chúng tôi đang tạo một phương thức ẩn danh hoàn toàn chỉ để thực hiện một cuộc gọi khác đến phương thức println(String).
Xem xét println(String) chính nó là một phương pháp mất String và trả về Unit, người ta sẽ nghĩ rằng chúng tôi có thể nén điều này xuống một chút. Khi nó quay ra, chúng ta có thể:

iterate(a, println) 

Bằng cách bỏ qua các dấu ngoặc đơn và chỉ định tên phương pháp, chúng ta đang nói trình biên dịch Scala mà chúng ta muốn sử dụng println như một giá trị chức năng, đi qua nó phương pháp iterate.
Vì vậy, thay vì tạo phương thức mới chỉ để xử lý một nhóm cuộc gọi, chúng tôi chuyển một phương pháp cũ đã thực hiện những gì chúng tôi muốn.
Đây là mẫu thường thấy trong C và C++. Trong thực tế, cú pháp để truyền một hàm như một giá trị hàm là chính xác như nhau. Dường như một số điều không bao giờ thay đổi…

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