ฟังก์ชั่นการใช้งานใน Scala คืออะไร?


311

ฉันไม่เคยเข้าใจจากคำนามที่ไม่ได้พูดและไม่จริง ( AddTwoชั้นมีตัวอย่างapplyที่เพิ่มสอง!)

ฉันเข้าใจว่ามันเป็นน้ำตาลซินแทคติคดังนั้น (ฉันอนุมานจากบริบท) มันต้องได้รับการออกแบบมาเพื่อให้โค้ดบางอย่างใช้งานง่ายขึ้น

คลาสที่มีapplyฟังก์ชั่นให้ความหมายอะไร? ใช้เพื่ออะไรและมีจุดประสงค์อะไรในการปรับปรุงโค้ดให้ดีขึ้น (unmarshalling, verbing นาม ฯลฯ )

มันช่วยได้อย่างไรเมื่อใช้ในวัตถุที่แสดงร่วม


4
แม้แต่ googling ที่รวดเร็วก็นำบทความดีๆมามากมาย นี่คือหนึ่ง: jackcoughonsoftware.blogspot.com/2009/01/…
om-nom-nom


6
จริง ๆ แล้วมันเป็นเหมือนกับ constructor ใน Java / C ++ แต่สามารถคืนค่าและมีชื่อ 'Apply'
ses

มันเป็นวิธีการไม่ใช่ฟังก์ชั่น ความแตกต่างระหว่างวิธีการและฟังก์ชั่นมีความสำคัญมากในสกาล่า
rightfold

1
ในการอธิบายรายละเอียดเกี่ยวกับ Zoidberg "วิธีการเป็นเพียงฟังก์ชั่นที่สามารถเข้าถึงสถานะของคลาส" twitter.github.io/scala_school/basics.htmlโดยทั่วไปจะใช้กับมากกว่า Scala stackoverflow.com/questions/155609/ …
DharmaTurtle

คำตอบ:


641

นักคณิตศาสตร์มีวิธีตลก ๆ ของตัวเองดังนั้นแทนที่จะพูดว่า "จากนั้นเราเรียกฟังก์ชั่นการfส่งผ่านมันxเป็นพารามิเตอร์" ตามที่เราโปรแกรมเมอร์จะบอกว่าพวกเขาพูดถึง "การใช้ฟังก์ชั่นfกับอาร์กิวเมนต์x"

ในวิชาคณิตศาสตร์และวิทยาศาสตร์คอมพิวเตอร์ Apply เป็นฟังก์ชันที่ใช้ฟังก์ชันกับอาร์กิวเมนต์
วิกิพีเดีย

applyให้บริการวัตถุประสงค์ของการปิดช่องว่างระหว่างกระบวนทัศน์เชิงวัตถุและเชิงหน้าที่ในสกาล่า ทุกฟังก์ชั่นใน Scala สามารถแสดงเป็นวัตถุได้ ฟังก์ชั่นทุกคนนอกจากนี้ยังมีประเภท OO: ยกตัวอย่างเช่นฟังก์ชั่นที่ใช้เวลาอีกด้วยIntพารามิเตอร์และส่งกลับจะมีประเภทของInt OOFunction1[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

เนื่องจากทุกอย่างเป็นวัตถุใน Scala ในfขณะนี้จึงสามารถใช้เป็นข้อมูลอ้างอิงกับFunction1[Int,Int]วัตถุได้ ตัวอย่างเช่นเราสามารถเรียกtoStringใช้เมธอดที่สืบทอดมาAnyซึ่งอาจเป็นไปไม่ได้สำหรับฟังก์ชั่นแท้เนื่องจากฟังก์ชั่นไม่มีวิธีการ:

  f.toString

หรือเราสามารถกำหนดFunction1[Int,Int]วัตถุอื่น โดยการเรียกcomposeใช้เมธอดบนfและผูกมัดฟังก์ชันที่แตกต่างกันสองฟังก์ชันเข้าด้วยกัน:

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

ตอนนี้ถ้าเราต้องการเรียกใช้ฟังก์ชันจริงหรือตามที่นักคณิตศาสตร์บอกว่า "ใช้ฟังก์ชั่นกับอาร์กิวเมนต์" เราจะเรียกใช้applyเมธอดบนFunction1[Int,Int]วัตถุ:

 f2.apply(2)

การเขียนf.apply(args)ทุกครั้งที่คุณต้องการใช้งานฟังก์ชั่นที่แสดงเป็นวัตถุนั้นเป็นวิธีที่มุ่งเน้นวัตถุ แต่จะเพิ่มความยุ่งเหยิงจำนวนมากให้กับรหัสโดยไม่ต้องเพิ่มข้อมูลเพิ่มเติมและมันก็ดีที่จะใช้สัญลักษณ์มาตรฐานมากขึ้นเช่น f(args)ในขณะที่ นั่นคือสิ่งที่ขั้นตอนคอมไพเลอร์สกาล่าและเมื่อใดก็ตามที่เรามีการอ้างอิงfไปยังวัตถุที่ฟังก์ชั่นและการเขียนf (args)เพื่อนำไปใช้การขัดแย้งในการทำงานที่เป็นตัวแทนของคอมไพเลอร์เงียบ ๆ ขยายไปยังวิธีการโทรวัตถุf (args)f.apply (args)

ทุกฟังก์ชั่นใน Scala สามารถใช้เป็นวัตถุได้และทำงานในลักษณะอื่นเช่นกัน - วัตถุทุกชิ้นสามารถใช้เป็นฟังก์ชันได้หากมีapplyวิธีการ วัตถุดังกล่าวสามารถใช้ในสัญกรณ์ฟังก์ชั่น:

// 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 

มีหลายกรณีการใช้งานเมื่อเราต้องการที่จะถือว่าวัตถุเป็นฟังก์ชั่น สถานการณ์ที่พบมากที่สุดคือรูปแบบโรงงาน แทนที่จะเพิ่มความยุ่งเหยิงในรหัสโดยใช้วิธีการจากโรงงานเราสามารถapplyคัดค้านชุดของการขัดแย้งเพื่อสร้างตัวอย่างใหม่ของคลาสที่เกี่ยวข้อง:

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) 

ดังนั้นapplyวิธีการจึงเป็นวิธีที่สะดวกในการปิดช่องว่างระหว่างฟังก์ชั่นและวัตถุใน Scala


1
คุณจะเพิ่มประเภทตัวแปรที่กำหนดเองลงในวัตถุได้อย่างไร AFAIK มันเป็นไปไม่ได้ นี่คือตัวอย่าง: คุณมีคลาสclass Average[YType](yZeroValue:YType)นี้ คุณจะส่ง YType จากวัตถุนั้นได้อย่างไรเพราะวัตถุไม่สามารถพิมพ์พารามิเตอร์ได้
เอเดรียน

ประเภทใน Scala มักจะสามารถอนุมานได้ตามอาร์กิวเมนต์ แต่ถ้าไม่ใช่คุณสามารถระบุได้ผ่านวงเล็บเหลี่ยม ดังนั้นเมื่ออินสแตนซ์วัตถุเฉลี่ยคุณสามารถพูดว่า "val avg = new เฉลี่ย [Int] (0)"
Angelo Genovese

4
คลาสเคสมีค่าที่กล่าวถึงที่นี่ คลาสเคสช่วยให้สะดวกเพิ่มอัตโนมัติและเพิ่มขึ้นapplyและunapplyวิธีการในวัตถุที่เป็นคู่หูของชั้นเรียน (เหนือสิ่งอื่นใด) โดยอัตโนมัติ ดังนั้นการกำหนดระดับที่มีเพียงหนึ่งบรรทัดเช่นนี้ทำให้มันเป็นไปได้ในการผลิตวัตถุใหม่ของการเรียนรถยนต์โดยเพียงแค่:case class Car(make:String, model: String, year: Short, color: String) val myCar = Car("VW","Golf",1999,"Blue")นี่เป็นการจดชวเลขสำหรับCar.apply(...)
Kjetil S.

1
every object can be treated as a function, provided it has the apply method- ตอนนี้เหมาะสมแล้ว
Arj


51

มันมาจากแนวคิดที่ว่าคุณมักจะต้องการนำบางสิ่งไปใช้กับวัตถุ ตัวอย่างที่ถูกต้องมากขึ้นคือหนึ่งในโรงงาน เมื่อคุณมีโรงงานคุณต้องการใช้พารามิเตอร์เพื่อสร้างวัตถุ

พวก Scala applyคิดว่าในขณะที่มันเกิดขึ้นในสถานการณ์ที่หลายคนก็อาจจะดีที่จะมีทางลัดไปยังโทร ดังนั้นเมื่อคุณให้พารามิเตอร์โดยตรงกับวัตถุมันก็จะได้รับการออกแบบราวกับว่าคุณส่งพารามิเตอร์เหล่านี้ไปยังฟังก์ชั่นการใช้งานของวัตถุนั้น:

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

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

มันมักจะใช้ในวัตถุที่แสดงร่วมเพื่อให้วิธีการที่ดีจากโรงงานสำหรับชั้นเรียนหรือลักษณะนี่คือตัวอย่าง:

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
  }
}

จากไลบรารี่มาตรฐานของสกาล่าคุณอาจดูว่าscala.collection.Seqมีการนำไปใช้อย่างไร: Seqเป็นคุณลักษณะดังนั้นnew Seq(1, 2)จะไม่รวบรวม แต่ต้องขอบคุณวัตถุที่เป็นคู่หูและใช้คุณสามารถโทรSeq(1, 2)และการนำไปใช้นั้นจะถูกเลือกโดยวัตถุที่แสดงร่วม


สามารถใช้การเทียบเท่าโดยนัยได้ด้วย Implicits หรือไม่?
jayunit100

11

นี่เป็นตัวอย่างเล็ก ๆ สำหรับผู้ที่ต้องการอ่านอย่างรวดเร็ว

 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")


} 

เอาท์พุต

greeter-1 กำลังถูกสร้างอินสแตนซ์ด้วยข้อความสวัสดี

รู้ตัว -2 กำลังยกตัวอย่างกับโลกแห่งข้อความ


2
ข้อความสำหรับ g2 ถูกต้องหรือไม่? ที่จริงแล้วเกิดขึ้นที่การสร้างอินสแตนซ์หรือในความเป็นจริงหลังจากการสร้างอินสแตนซ์ (แม้ว่าจะเป็นอินสแตนซ์ที่ขี้เกียจ)?
ทิม Barrass
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.