Jaka jest funkcja zastosowania w Scali?

311

Nigdy nie zrozumiałem tego na podstawie wymyślonych rzeczowników AddTwoodznaczania i sprawdzania ( klasa ma applydwa, które dodają dwa!) Przykłady.

Rozumiem, że to cukier syntaktyczny, więc (wydedukowałem z kontekstu) musiał być zaprojektowany tak, aby niektóre kody były bardziej intuicyjne.

Jakie znaczenie ma klasa z applyfunkcją? Do czego służy i do jakich celów poprawia kod (odznaczanie, sprawdzanie rzeczowników itp.)?

jak to pomaga, gdy jest używane w obiekcie towarzyszącym?

Jesvin Jose
źródło
4
Nawet szybkie googling przynosi wiele fajnych artykułów. Oto jeden: jackcoughonsoftware.blogspot.com/2009/01/…
om-nom-nom
6
w rzeczywistości jest taki sam jak konstruktor w Javie / C ++, ale może zwrócić wartość i ma nazwę „zastosuj”
ses
To metoda, a nie funkcja. Rozróżnienie między metodami i funkcjami jest bardzo ważne w Scali.
prawej
1
Aby opracować Zoidberg, „Metody to tylko funkcje, które mogą uzyskać dostęp do stanu klasy” twitter.github.io/scala_school/basics.html Zasadniczo dotyczy to więcej niż Scala stackoverflow.com/questions/155609/...
DharmaTurtle

Odpowiedzi:

640

Matematycy mają swoje własne zabawne sposoby, więc zamiast mówić „wtedy funkcję nazywamy fprzekazując ją xjako parametr”, jak powiedzieliby to programiści, mówią o „zastosowaniu funkcji fdo jej argumentu x”.

W matematyce i informatyce Apply to funkcja, która stosuje funkcje do argumentów.
Wikipedia

applysłuży do zlikwidowania luki między paradygmatami obiektowymi i funkcjonalnymi w Scali. Każda funkcja w Scali może być reprezentowana jako obiekt. Każda funkcja ma również typ OO: na przykład funkcja, która pobiera Intparametr i zwraca an, Intbędzie miała typ 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

Ponieważ w Scali wszystko jest przedmiotem, fmożna je teraz traktować jako odniesienie do Function1[Int,Int]obiektu. Na przykład możemy wywołać toStringmetodę odziedziczoną po Any, która byłaby niemożliwa dla czystej funkcji, ponieważ funkcje nie mają metod:

  f.toString

Lub możemy zdefiniować inny Function1[Int,Int]obiekt, wywołując composemetodę fi łącząc dwie różne funkcje razem:

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

Teraz, jeśli chcemy faktycznie wykonać funkcję lub, jak matematyk mówi „zastosuj funkcję do jej argumentów”, wywołalibyśmy applymetodę na Function1[Int,Int]obiekcie:

 f2.apply(2)

Pisanie za f.apply(args)każdym razem, gdy chcesz wykonać funkcję reprezentowaną jako obiekt, jest zorientowane obiektowo, ale dodałoby dużo bałaganu do kodu bez dodawania wielu dodatkowych informacji i byłoby fajnie móc użyć bardziej standardowej notacji, takiej jak jak f(args). Właśnie tam wkracza kompilator Scala, a gdy tylko mamy odwołanie fdo obiektu funkcji i piszemy, f (args)aby zastosować argumenty do reprezentowanej funkcji, kompilator po cichu rozwija się f (args)do wywołania metody obiektu f.apply (args).

Każda funkcja w Scali może być traktowana jako obiekt i działa również w drugą stronę - każdy obiekt może być traktowany jako funkcja, pod warunkiem, że ma taką applymetodę. Takie obiekty mogą być używane w notacji funkcji:

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

Istnieje wiele przypadków użycia, w których chcielibyśmy traktować obiekt jako funkcję. Najczęstszym scenariuszem jest wzór fabryczny . Zamiast dodawać bałaganu do kodu za pomocą metody fabrycznej, możemy applysprzeciwić się zestawowi argumentów, aby utworzyć nową instancję powiązanej klasy:

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) 

Dlatego applymetoda jest po prostu wygodnym sposobem na zlikwidowanie luki między funkcjami a obiektami w Scali.

Vlad Gudim
źródło
1
jak dodasz niestandardowy typ zmiennej do obiektu. AFAIK to niemożliwe. Oto przykład: masz tę klasę class Average[YType](yZeroValue:YType). Jak przekazać YType ze swojego obiektu, skoro obiekty nie mogą przyjmować parametrów typu?
Adrian
Typy w Scali zwykle można wywnioskować na podstawie argumentów, ale jeśli nie, możesz podać je w nawiasach kwadratowych. dlatego podczas tworzenia wystąpienia obiektu Average można powiedzieć „val avg = new Average [Int] (0)”
Angelo Genovese
4
Warto wspomnieć o klasach przypadków. Klasy przypadków wygodnie, automagicznie i niewidocznie dodaje applyi unapplymetody do obiektu towarzyszącego klasy (między innymi). Tak definiując klasę z tylko jedną linię jak ta case class Car(make:String, model: String, year: Short, color: String)umożliwia produkcję nowych obiektów klasy samochodów po prostu: val myCar = Car("VW","Golf",1999,"Blue"). To jest skrót odCar.apply(...)
Kjetil S.
1
every object can be treated as a function, provided it has the apply method- tyle teraz ma sens.
Arj
patrz także slideshare.net/pjschwarz/…
Philip Schwarz
51

Wynika to z faktu, że często chcesz zastosować coś do obiektu. Bardziej dokładnym przykładem jest fabryka. Gdy masz fabrykę, chcesz zastosować do niej parametr, aby utworzyć obiekt.

Scala faceci myśleli, że jak to często bywa w wielu sytuacjach, fajnie byłoby mieć skrót do połączenia apply. Zatem, gdy podajesz parametry bezpośrednio do obiektu, jest on wyłączany tak, jakbyś przekazał te parametry do funkcji zastosuj tego obiektu:

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

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

Jest często używany w obiekcie towarzyszącym, aby zapewnić miłą fabryczną metodę dla klasy lub cechy, oto przykład:

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

Ze standardowej biblioteki scala możesz zobaczyć, jak scala.collection.Seqjest implementowany: Seqjest cechą, dlatego new Seq(1, 2)nie będzie się kompilował, ale dzięki obiektowi towarzyszącemu i zastosowaniu możesz wywoływać, Seq(1, 2)a implementacja jest wybierana przez obiekt towarzyszący.

Nicolas
źródło
Czy ekwiwalent zastosowania można również osiągnąć za pomocą Implicits?
jayunit100,
11

Oto mały przykład dla tych, którzy chcą szybko przejrzeć

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


} 

wynik

Zostaje utworzona instancja Greeter-1 z komunikatem hello

Program witający-2 jest tworzony w świecie wiadomości

sdinesh94
źródło
2
Czy komunikat dla g2 jest poprawny? Czy dzieje się tak naprawdę w momencie tworzenia instancji, czy też w rzeczywistości po utworzeniu instancji (nawet jeśli jest to leniwie utworzone)?
Tim Barrass