Sự khác biệt giữa ba cách xác định một hàm trong Scala


92

Đưa ra ba cách thể hiện cùng một hàm f(a) := a + 1:

val f1 = (a:Int) => a + 1
def f2 = (a:Int) => a + 1
def f3:(Int => Int) = a => a + 1

Làm thế nào để các định nghĩa này khác nhau? REPL không chỉ ra bất kỳ sự khác biệt rõ ràng nào:

scala> f1
res38: (Int) => Int = <function1>
scala> f2
res39: (Int) => Int = <function1>
scala> f3
res40: (Int) => Int = <function1>

11
Bạn cần lưu ý rằng trong khối thứ 2 ở trên, việc đánh giá f1trong REPL hiển thị giá trị được liên kết tĩnh f1trong khi đánh giá f2f3hiển thị kết quả của việc gọi các phương thức đó. Cụ thể, một phiên bản mới Function1[Int, Int]được tạo ra mỗi khi f2hoặc f3được gọi ra, trong khi đó f1là phiên bản Function1[Int, Int]vĩnh viễn.
Randall Schulz

@RandallSchulz cho rằng phiên bản val không yêu cầu phiên bản hàm mới, tại sao người ta lại sử dụng def trong trường hợp này?
điêu luyện

2
@virtualeyes Tình huống duy nhất mà tôi có thể nhớ lại là nơi người ta thấy các giá trị định nghĩa mang lại các giá trị FunctionN [...] nằm trong thư viện phân tích cú pháp tổ hợp. Rất không phổ biến khi viết các phương thức mang lại các hàm và hầu như không bao giờ người ta sử dụng một định nghĩa để mang lại nhiều bản sao của một hàm không thay đổi về mặt ngữ nghĩa / chức năng.
Randall Schulz

Câu trả lời:


112

f1 là một hàm nhận một số nguyên và trả về một số nguyên.

f2là một phương thức không có arity trả về một hàm nhận một số nguyên và trả về một số nguyên. (Khi bạn nhập f2REPL sau đó, nó sẽ trở thành một lệnh gọi đến phương thức f2.)

f3giống như f2. Bạn chỉ không sử dụng kiểu suy luận ở đó.


6
Tại sao f1là a functionf2là a method?
Freewind

17
@Freewind, một hàm là một đối tượng có tên phương thức apply. Một phương pháp, tốt, là một phương pháp.
missfaktor

Câu trả lời tuyệt vời. Câu hỏi: bạn nói f2 không có arity, nhưng nó không phải là arity? vi.wikipedia.org/wiki/Arity "Một hàm nullary không có đối số. Một hàm đơn phân có một đối số." Chỉ tò mò thôi!
Matthew Cornell

5
@MatthewCornell, f2bản thân nó không chấp nhận đối số. Đối tượng hàm mà nó trả về không.
missfaktor

122

Bên trong một lớp, valđược đánh giá khi khởi tạo trong khi chỉ defđược đánh giá khi và mọi lúc , hàm được gọi. Trong đoạn mã dưới đây, bạn sẽ thấy rằng x được đánh giá lần đầu tiên đối tượng được sử dụng, nhưng không phải lần nữa khi thành viên x được truy cập. Ngược lại, y không được đánh giá khi đối tượng được khởi tạo, nhưng được đánh giá mỗi khi thành viên được truy cập.

  class A(a: Int) {
    val x = { println("x is set to something"); a }
    def y = { println("y is set to something"); a }
  }

  // Prints: x is set to something
  val a = new A(1)

  // Prints: "1"
  println(a.x)

  // Prints: "1"                               
  println(a.x)

  // Prints: "y is set to something" and "1"                                  
  println(a.y)

  // Prints: "y is set to something" and "1"                                                                                   
  println(a.y)

@JacobusR có phải điều này chỉ đúng trong một lớp không?
Andrew Cassidy,

ví dụ: scala> var b = 5 b: Int = 5 scala> val a: (Int => Int) = x => x + ba: Int => Int = <function1> scala> a (5) res48: Int = 10 scala> b = 6 b: Int = 6 scala> a (5) res49: Int = 11 tôi đã chờ đợi một (5) để trở về 10 và giá trị của b đã được inlined
Andrew Cassidy

@AndrewCassidy hàm alà không thay đổi và được đánh giá khi khởi tạo, nhưng bvẫn là một giá trị có thể thay đổi. Vì vậy, tham chiếu tới bđược đặt trong quá trình khởi tạo, nhưng giá trị được lưu trữ bởi bvẫn có thể thay đổi. Để giải trí, bây giờ bạn có thể tạo một cái mới val b = 123. Sau đó, ý muốn của bạn a(5)luôn cho 11, vì bbây giờ là một giá trị hoàn toàn mới.
Jack

@JacobusR cảm ơn ... điều này có ý nghĩa. Điều này trùng với định nghĩa về "phạm vi từ vựng" vì hàm a mang một tham chiếu đến "var b" ban đầu. Tôi đoán điều khiến tôi bối rối là: var b = 5; val c = b; b = 6; hành động khác nhau. Tôi đoán rằng tôi không nên mong đợi một định nghĩa hàm mang các tham chiếu đến phạm vi "từ vựng" ban đầu để hoạt động giống như một Int.
Andrew Cassidy

3

Việc thực thi một định nghĩa như def x = e sẽ không đánh giá biểu thức e . Thay vào đó e được đánh giá bất cứ khi nào x được sử dụng. Ngoài ra, Scala đưa ra định nghĩa giá trị val x = e , định nghĩa này đánh giá phía bên phải e như một phần của đánh giá định nghĩa. Nếu x được sử dụng sau đó, nó ngay lập tức được thay thế bằng giá trị được tính trước của e , do đó biểu thức không cần phải đánh giá lại.

Scala bằng ví dụ của Martin Odersky

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.