2012-03-16 3 views
230

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

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

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

Как это можно использовать при использовании в компаньоне?

+4

Даже быстрый поиск в Google приносит много хорошие статьи. Вот один из них: http://jackcoughonsoftware.blogspot.com/2009/01/deeper-look-at-apply-method-in-scala.html –

+0

http://stackoverflow.com/questions/1223834/how-does- scalas-apply-method-magic-work – ses

+4

Фактически это то же самое, что и конструктор в Java/C++, но может возвращать значение и имеет «применить» имя – ses

ответ

508

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

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

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. Вместо добавление помех к коде с помощью метода производства возможен 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 из своего объекта, поскольку объекты не могут принимать параметры типа? – Adrian

+0

Типы в Scala обычно можно вывести на основе аргументов, но если вы не можете их предоставить через квадратные скобки. поэтому при создании экземпляра Среднего объекта вы можете сказать: «val avg = new Average [Int] (0)» –

+3

Примеры занятий заслуживают упоминания здесь. Классы классов удобно, автоматически и невидимо добавляют методы «apply» и «unapply» к сопутствующему объекту класса (среди прочего). Таким образом, определение класса с одной строкой, такой как этот класс case car (make: String, model: String, year: Short, color: String), позволяет создавать новые объекты класса Car просто: 'val myCar = Автомобиль ("VW", "Гольф", 1999, "Blue") '. Это сокращенно для 'Car.apply (...)' –

37

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

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

+0

Может ли эквивалент применения быть выполнен с использованием Implicits? – jayunit100

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

+0

Правильно ли это сообщение для g2? Действительно ли это происходит при создании экземпляра, или это действительно после создания экземпляра (даже если он лениво инстанцируется)? –

Смежные вопросы