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

222

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

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

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

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

  • 4
    Даже быстрый поиск в Google приносит много хороших статей. Вот один из них: jackcoughonsoftware.blogspot.com/2009/01/…
  • 0
    stackoverflow.com/questions/1223834/...
Показать ещё 3 комментария
Теги:

3 ответа

517
Лучший ответ

У математиков есть свои маленькие забавные способы, поэтому вместо того, чтобы говорить "тогда мы вызываем функцию 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 

Существует много случаев использования, когда мы хотели бы рассматривать объект как функцию. Наиболее распространенным сценарием является factory pattern. Вместо добавления беспорядка в код с помощью метода factory мы можем apply object к набору аргументов для создания нового экземпляра связанного класса:

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

Это исходит из идеи, что вы часто хотите применить что-то к объекту. Более точный пример - один из заводов. Когда у вас есть factory, вы хотите применить к нему параметр для создания объекта.

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)

Он часто используется в сопутствующем объекте, чтобы обеспечить хороший метод factory для класса или признака, вот пример:

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) и реализация выбирается сопутствующим объектом.

  • 0
    Может ли эквивалент применения применяться также с помощью Implicits?
3

Вот небольшой пример для тех, кто хочет быстро изучить

 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 создается с миром сообщений

  • 1
    Является ли сообщение для g2 правильным? Это на самом деле происходит при создании экземпляра или после создания (даже если оно выполняется лениво)?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню