Яка функція застосування у Scala?


311

Я ніколи не розумів цього з надуманих іменників беззмістовної та дієслівної роботи ( AddTwoклас має applyдодавання двох!).

Я розумію, що це синтаксичний цукор, тому (я вийшов з контексту) він, мабуть, був розроблений, щоб зробити якийсь код більш інтуїтивним.

Яке значення надає клас із applyфункцією? Для чого він використовується, і для яких цілей він вдосконалює код (беззмістовний, дієслівний іменник тощо)?

як це допомагає при використанні в супутньому об’єкті?


4
Навіть швидкий гуглінг приносить безліч приємних статей. Ось один: jackcoughonsoftware.blogspot.com/2009/01/…
om-nom-nom


6
на самому ділі це так само , як конструктор в Java / C ++ , але може повернути значення і має «застосувати» ім'я
SES

Це метод, а не функція. Відмінність методів та функцій дуже важлива у Scala.
праворуч

1
Щоб уточнити Зойдберг, «Методи просто функції , які можуть отримати доступ до стану класу» twitter.github.io/scala_school/basics.html В цілому це відноситься до більш ніж Scala stackoverflow.com/questions/155609 / ...
DharmaTurtle

Відповіді:


640

У математиків є свої маленькі кумедні способи, тому замість того, щоб говорити "тоді ми називаємо функцію, fпередаючи її xяк параметр", як ми говорили програмісти, вони говорять про "застосуванні функції fдо її аргументу x".

У математиці та інформатиці застосувати функцію, яка застосовує функції до аргументів.
Вікіпедія

applyслужить метою усунення розриву між об'єктно-орієнтованими та функціональними парадигмами у Scala. Кожна функція в Scala може бути представлена ​​як об'єкт. Кожна функція також має тип OO: наприклад, функція, яка приймає Intпараметр і повертає Int, матиме тип 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

Оскільки в 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). Ось де укладається компілятор Scala і кожного разу, коли ми маємо посилання 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 Average [Int] (0)"
Angelo Genovese

4
Тут слід згадати класи кейсів. Класи кейсів зручно, автоматично та невидимо додають applyта unapplyметоди до супутнього об’єкта класу (серед іншого). Таким чином , визначивши клас з одним рядком , як це case class Car(make:String, model: String, year: Short, color: String)дозволяє створювати нові об'єкти класу Car, просто: 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- стільки зараз має сенс.
Арж


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 ви можете подивитися, як scala.collection.Seqреалізовано: Seqчи є ознака, тому new Seq(1, 2)вона не компілюється, але завдяки супутниковому об'єкту та застосуванню ви можете зателефонувати, Seq(1, 2)а реалізацію буде обрано супутнім об’єктом.


Чи може еквівалент застосування застосовуватися також за допомогою імпліцитів?
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")


} 

вихід

З вітальним словом привітається повідомлення 1

Вітаю-2 інстанціюється світом повідомлень


2
Чи правильне повідомлення для g2? Це насправді відбувається в інстанції, чи це насправді після інстанції (навіть якщо це ліниво встановлено)?
Тім Баррасс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.