Что такое функция применения в Scala?


311

Я никогда не понимал этого из придуманных, неуместных и глагольных существительных (у AddTwoкласса есть класс, applyкоторый добавляет два!) Примеров.

Я понимаю, что это синтаксический сахар, поэтому (я понял из контекста) он должен был быть разработан, чтобы сделать некоторый код более интуитивным.

Какой смысл дает класс с applyфункцией? Для чего он используется и в каких целях он делает код лучше (демаршаллинг, глагольные существительные и т. Д.)?

Как это помогает при использовании в объекте-компаньоне?


4
Даже быстрый поиск в Google приносит много хороших статей. Вот один из них: jackcoughonsoftware.blogspot.com/2009/01/…
om-nom-nom


6
на самом деле он такой же, как конструктор в Java / C ++, но может возвращать значение и имеет имя 'apply'
ses

Это метод, а не функция. Различие между методами и функциями очень важно в Scala.
rightfold

1
Если говорить более подробно о Зойдберге, «Методы - это просто функции, которые могут получить доступ к состоянию класса» twitter.github.io/scala_school/basics.html В общем, это относится не только к Scala stackoverflow.com/questions/155609/…
DharmaTurtle

Ответы:


641

У математиков есть свои маленькие забавные способы, поэтому вместо того, чтобы говорить «тогда мы вызываем функцию, fпередавая ее xв качестве параметра», как сказали бы мы, программисты, они говорят о «применении функции fк ее аргументу x».

В математике и информатике Apply - это функция, которая применяет функции к аргументам.
Википедия

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 обычно выводятся на основании аргументов, но если нет, вы можете указать их в квадратных скобках. поэтому, когда вы создаете экземпляр объекта Average, вы можете сказать «val avg = new Average [Int] (0)»
Angelo Genovese

4
Кейс-классы стоит упомянуть здесь. Классы Case удобно, автоматически и незаметно добавляют 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. Таким образом, когда вы передаете параметры непосредственно объекту, это обескураживается, как если бы вы передавали эти параметры функции 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)не компилируется, но благодаря объекту-компаньону и apply вы можете вызывать, 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 создается с сообщением hello

Greeter-2 создается с сообщением мира


2
Является ли сообщение для g2 правильным? Это на самом деле происходит при создании экземпляра или после создания (даже если оно выполняется лениво)?
Тим Баррасс
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.