Hàm áp dụng trong Scala là gì?


311

Tôi không bao giờ hiểu nó từ các danh từ không được sắp xếp và xác minh (một AddTwolớp có applythêm hai ví dụ!).

Tôi hiểu rằng đó là đường cú pháp, vì vậy (tôi đã suy luận từ ngữ cảnh) nó phải được thiết kế để làm cho một số mã trực quan hơn.

Một lớp có applychức năng cho ý nghĩa gì ? Nó được sử dụng để làm gì, và mục đích nào làm cho mã tốt hơn (không sắp xếp, danh từ xác minh, v.v.)?

Nó giúp ích như thế nào khi được sử dụng trong một đối tượng đồng hành?


4
Ngay cả một googling nhanh chóng mang lại rất nhiều bài viết tốt đẹp. Đây là một: jackcoughonsoftware.blogspot.com/2009/01/ Kẻ
om-nom-nom


6
thực tế nó giống như một hàm tạo trong Java / C ++ nhưng có thể trả về giá trị và có tên 'áp dụng'
Ses

Đó là một phương pháp, không phải là một chức năng. Sự khác biệt giữa các phương thức và chức năng là rất quan trọng trong Scala.
đúng

1
Để giải thích trên Zoidberg, "Các phương thức chỉ là các hàm có thể truy cập trạng thái của lớp" twitter.github.io/scala_school/basics.html Nói chung, điều này áp dụng cho nhiều hơn Scala stackoverflow.com/questions/155609/ của
DharmaTurtle

Câu trả lời:


641

Các nhà toán học có những cách hài hước nhỏ của riêng họ, vì vậy thay vì nói "sau đó chúng ta gọi hàm ftruyền qua nó xnhư một tham số" như chúng ta lập trình viên sẽ nói, họ nói về "áp dụng hàm fcho đối số của nó x".

Trong toán học và khoa học máy tính, Áp dụng là một hàm áp dụng các hàm cho các đối số.
Wikipedia

applyphục vụ mục đích thu hẹp khoảng cách giữa các mô hình hướng đối tượng và chức năng trong Scala. Mọi chức năng trong Scala có thể được biểu diễn dưới dạng một đối tượng. Mỗi hàm cũng có một loại OO: ví dụ, một hàm lấy Inttham số và trả về Intsẽ có loại OO Function1[Int,Int].

 // define a function in scala
 (x:Int) => x + 1

 // assign an object representing the function to a variable
 val f = (x:Int) => x + 1

Vì mọi thứ là một đối tượng trong Scala fgiờ đây có thể được coi là một tham chiếu đến Function1[Int,Int]đối tượng. Ví dụ: chúng ta có thể gọi toStringphương thức được kế thừa từ Anyđó, điều đó là không thể đối với một hàm thuần túy, bởi vì các hàm không có các phương thức:

  f.toString

Hoặc chúng ta có thể định nghĩa một Function1[Int,Int]đối tượng khác bằng cách gọi composephương thức trên fvà nối hai hàm khác nhau lại với nhau:

 val f2 = f.compose((x:Int) => x - 1)

Bây giờ nếu chúng ta thực sự muốn thực thi hàm, hoặc như nhà toán học nói "áp dụng một hàm cho các đối số của nó", chúng ta sẽ gọi applyphương thức trên Function1[Int,Int]đối tượng:

 f2.apply(2)

Viết f.apply(args)mỗi khi bạn muốn thực thi một hàm được biểu diễn dưới dạng một đối tượng là cách hướng đối tượng, nhưng sẽ thêm rất nhiều lộn xộn vào mã mà không cần thêm nhiều thông tin bổ sung và thật tuyệt khi có thể sử dụng ký hiệu chuẩn hơn, như vậy như f(args). Đó là nơi trình biên dịch Scala bước vào và bất cứ khi nào chúng ta có một tham chiếu fđến một đối tượng hàm và viết f (args)để áp dụng các đối số cho hàm được biểu diễn, trình biên dịch sẽ âm thầm mở rộng f (args)cho lệnh gọi phương thức đối tượng f.apply (args).

Mọi chức năng trong Scala đều có thể được coi là một đối tượng và nó cũng hoạt động theo cách khác - mọi đối tượng có thể được coi là một chức năng, miễn là nó có applyphương thức. Các đối tượng như vậy có thể được sử dụng trong ký hiệu chức năng:

// we will be able to use this object as a function, as well as an object
object Foo {
  var y = 5
  def apply (x: Int) = x + y
}


Foo (1) // using Foo object in function notation 

Có nhiều trường hợp sử dụng khi chúng ta muốn coi một đối tượng là một hàm. Kịch bản phổ biến nhất là một mô hình nhà máy . Thay vì thêm lộn xộn vào mã bằng phương thức nhà máy, chúng ta có thể applyphản đối một tập hợp các đối số để tạo một thể hiện mới của một lớp liên kết:

List(1,2,3) // same as List.apply(1,2,3) but less clutter, functional notation

// the way the factory method invocation would have looked
// in other languages with OO notation - needless clutter
List.instanceOf(1,2,3) 

Vì vậy, applyphương thức chỉ là một cách tiện dụng để thu hẹp khoảng cách giữa các hàm và đối tượng trong Scala.


1
Làm thế nào bạn sẽ thêm loại biến tùy chỉnh cho một đối tượng. AFAIK không thể. Đây là một ví dụ: Bạn có lớp này class Average[YType](yZeroValue:YType). Làm thế nào để bạn vượt qua YType từ đối tượng của nó vì các đối tượng không thể lấy tham số loại?
Adrian

Các loại trong Scala thường có thể được suy luận dựa trên các đối số, nhưng nếu không, bạn có thể cung cấp chúng thông qua dấu ngoặc vuông. vì vậy, khi khởi tạo một đối tượng Trung bình, bạn có thể nói "val avg = new Average [Int] (0)"
Angelo Genovese

4
Các lớp học có giá trị được đề cập ở đây. Các lớp trường hợp thuận tiện, tự động và vô hình thêm applyunapplycác phương thức cho một đối tượng đồng hành của lớp (trong số những thứ khác). Vì vậy, việc xác định một lớp chỉ với một dòng như thế này case class Car(make:String, model: String, year: Short, color: String)có thể tạo ra các đối tượng mới của lớp Xe chỉ bằng : val myCar = Car("VW","Golf",1999,"Blue"). Đây là cách viết tắt củaCar.apply(...)
Kjetil S.

1
every object can be treated as a function, provided it has the apply method- rất nhiều bây giờ có ý nghĩa.
Arj


51

Nó xuất phát từ ý tưởng rằng bạn thường muốn áp dụng một cái gì đó cho một đối tượng. Ví dụ chính xác hơn là một trong những nhà máy. Khi bạn có một nhà máy, bạn muốn áp dụng tham số cho nó để tạo một đối tượng.

Những người Scala nghĩ rằng, vì nó xảy ra trong nhiều tình huống, thật tuyệt khi có một phím tắt để gọi apply. Do đó, khi bạn cung cấp tham số trực tiếp cho một đối tượng, nó sẽ bị tách ra như thể bạn chuyển các tham số này cho chức năng áp dụng của đối tượng đó:

class MyAdder(x: Int) {
  def apply(y: Int) = x + y
}

val adder = new MyAdder(2)
val result = adder(4) // equivalent to x.apply(4)

Nó thường được sử dụng trong đối tượng đồng hành, để cung cấp một phương thức xuất xưởng đẹp cho một lớp hoặc một đặc điểm, đây là một ví dụ:

trait A {
  val x: Int
  def myComplexStrategy: Int
}

object A {
  def apply(x: Int): A = new MyA(x)

  private class MyA(val x: Int) extends A {
    val myComplexStrategy = 42
  }
}

Từ thư viện tiêu chuẩn scala, bạn có thể xem cách scala.collection.Seqtriển khai: Seqlà một đặc điểm, do đó new Seq(1, 2)sẽ không biên dịch nhưng nhờ có đối tượng đồng hành và áp dụng, bạn có thể gọi Seq(1, 2)và việc triển khai được chọn bởi đối tượng đồng hành.


Có thể tương đương với việc áp dụng bằng Implicits không?
jayunit100

11

Đây là một ví dụ nhỏ cho những người muốn kiểm tra nhanh

 object ApplyExample01 extends App {


  class Greeter1(var message: String) {
    println("A greeter-1 is being instantiated with message " + message)


  }

  class Greeter2 {


    def apply(message: String) = {
      println("A greeter-2 is being instantiated with message " + message)
    }
  }

  val g1: Greeter1 = new Greeter1("hello")
  val g2: Greeter2 = new Greeter2()

  g2("world")


} 

đầu ra

Một lời chào-1 đang được khởi tạo với tin nhắn xin chào

Một lời chào-2 đang được khởi tạo với thế giới tin nhắn


2
Thông điệp cho g2 có đúng không? Đó có thực sự xảy ra tại thời điểm khởi tạo, hay thực tế là sau khi khởi tạo (ngay cả khi nó nhanh chóng được khởi tạo)?
Tim Barrass
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.