Was ist die Apply-Funktion in Scala?


311

Ich habe es nie anhand der erfundenen Beispiele für das Nicht-Marshalling und Verbieren von Substantiven (eine AddTwoKlasse hat eine apply, die zwei hinzufügt!) Verstanden .

Ich verstehe, dass es syntaktischer Zucker ist, also muss er (ich habe aus dem Kontext abgeleitet) entworfen worden sein, um Code intuitiver zu machen.

Welche Bedeutung hat eine Klasse mit einer applyFunktion? Wofür wird es verwendet und zu welchen Zwecken verbessert es den Code (Unmarshalling, Verbing von Substantiven usw.)?

Wie hilft es, wenn es in einem Begleitobjekt verwendet wird?


4
Selbst ein schnelles Googeln bringt viele schöne Artikel. Hier ist eine: jackcoughonsoftware.blogspot.com/2009/01/…
om-nom-nom


6
Eigentlich ist es dasselbe wie ein Konstruktor in Java / C ++, kann aber den Wert zurückgeben und hat den Namen 'apply'
ses

Es ist eine Methode, keine Funktion. Die Unterscheidung zwischen Methoden und Funktionen ist in Scala sehr wichtig.
Rechtsfalte

1
Um Zoidberg näher zu erläutern: "Methoden sind nur Funktionen, die auf den Status der Klasse zugreifen können" twitter.github.io/scala_school/basics.html Im Allgemeinen gilt dies für mehr als Scala stackoverflow.com/questions/155609/…
DharmaTurtle

Antworten:


641

Mathematiker haben ihre eigenen kleinen lustigen Wege. Anstatt zu sagen, "dann rufen wir die Funktion auf, findem wir sie xals Parameter übergeben", wie wir Programmierer sagen würden, sprechen sie über "Anwenden der Funktion fauf ihr Argument x".

In Mathematik und Informatik ist Anwenden eine Funktion, die Funktionen auf Argumente anwendet.
Wikipedia

applydient dazu, die Lücke zwischen objektorientierten und funktionalen Paradigmen in Scala zu schließen. Jede Funktion in Scala kann als Objekt dargestellt werden. Jede Funktion hat auch einen OO-Typ: Zum Beispiel hat eine Funktion, die einen IntParameter akzeptiert und einen zurückgibt Int, den OO-Typ von 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

Da in Scala alles ein Objekt ist, fkann es jetzt als Referenz auf ein Function1[Int,Int]Objekt behandelt werden. Zum Beispiel können wir toStringMethoden aufrufen , von denen geerbt Anywurde, was für eine reine Funktion unmöglich gewesen wäre, weil Funktionen keine Methoden haben:

  f.toString

Oder wir könnten ein anderes Function1[Int,Int]Objekt definieren, indem wir composemethod on aufrufen fund zwei verschiedene Funktionen miteinander verketten:

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

Wenn wir nun die Funktion tatsächlich ausführen möchten oder als Mathematiker sagen "eine Funktion auf ihre Argumente anwenden" sagen, würden wir die applyMethode für das Function1[Int,Int]Objekt aufrufen :

 f2.apply(2)

Das Schreiben f.apply(args)jedes Mal, wenn Sie eine als Objekt dargestellte Funktion ausführen möchten, ist objektorientiert, würde dem Code jedoch viel Unordnung hinzufügen, ohne viele zusätzliche Informationen hinzuzufügen, und es wäre schön, wenn Sie mehr Standardnotation verwenden könnten, wie z als f(args). Hier greift der Scala-Compiler ein, und wenn wir einen Verweis fauf ein Funktionsobjekt haben und schreiben f (args), um Argumente auf die dargestellte Funktion anzuwenden, erweitert der Compiler f (args)den Objektmethodenaufruf stillschweigend f.apply (args).

Jede Funktion in Scala kann als Objekt behandelt werden und funktioniert auch andersherum - jedes Objekt kann als Funktion behandelt werden, sofern es über die applyMethode verfügt. Solche Objekte können in der Funktionsnotation verwendet werden:

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

Es gibt viele Anwendungsfälle, in denen wir ein Objekt als Funktion behandeln möchten. Das häufigste Szenario ist ein Factory-Muster . Anstatt dem Code mithilfe einer Factory-Methode Unordnung hinzuzufügen, können wir applyeiner Reihe von Argumenten widersprechen, um eine neue Instanz einer zugeordneten Klasse zu erstellen:

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) 

Die applyMethode ist also nur eine praktische Möglichkeit, die Lücke zwischen Funktionen und Objekten in Scala zu schließen.


1
Wie würden Sie einem Objekt einen benutzerdefinierten Variablentyp hinzufügen? AFAIK ist nicht möglich. Hier ist ein Beispiel: Sie haben diese Klasse class Average[YType](yZeroValue:YType). Wie übergeben Sie YType von seinem Objekt, da Objekte keine Typparameter annehmen können?
Adrian

Typen in Scala können normalerweise anhand der Argumente abgeleitet werden. Wenn nicht, können Sie sie über die eckigen Klammern angeben. Wenn Sie also ein durchschnittliches Objekt instanziieren, können Sie sagen "val avg = new Average [Int] (0)"
Angelo Genovese

4
Fallklassen sind hier erwähnenswert. Fallklassen fügen einem Begleitobjekt der Klasse (unter anderem) bequem, automatisch und unsichtbar hinzu applyund unapplyMethoden. Wenn Sie also eine Klasse mit nur einer Zeile wie dieser definieren, können case class Car(make:String, model: String, year: Short, color: String)Sie neue Objekte der Car-Klasse erstellen, indem Sie einfach : val myCar = Car("VW","Golf",1999,"Blue"). Dies ist eine Abkürzung fürCar.apply(...)
Kjetil S.

1
every object can be treated as a function, provided it has the apply method- So viel macht jetzt Sinn.
Arj


51

Es kommt von der Idee, dass Sie oft etwas auf ein Objekt anwenden möchten . Das genauere Beispiel ist das der Fabriken. Wenn Sie eine Factory haben, möchten Sie Parameter darauf anwenden , um ein Objekt zu erstellen.

Scala-Leute dachten, dass es, wie es in vielen Situationen vorkommt, schön sein könnte, eine Abkürzung zum Anrufen zu haben apply. Wenn Sie also einem Objekt Parameter direkt zuweisen, wird es deaktiviert, als ob Sie diese Parameter an die Apply-Funktion dieses Objekts übergeben:

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

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

Es wird häufig in Begleitobjekten verwendet, um eine nette Factory-Methode für eine Klasse oder ein Merkmal bereitzustellen. Hier ein Beispiel:

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

In der Scala-Standardbibliothek können Sie sich ansehen, wie scala.collection.Seqimplementiert wird: SeqIst ein Merkmal, wird also new Seq(1, 2)nicht kompiliert, aber dank des Companion-Objekts und der Anwendung können Sie es aufrufen Seq(1, 2)und die Implementierung wird vom Companion-Objekt ausgewählt.


Könnte das Äquivalent von bewerben auch mit Implicits erreicht werden?
Jayunit100

11

Hier ist ein kleines Beispiel für diejenigen, die schnell lesen möchten

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


} 

Ausgabe

Ein Begrüßer-1 wird mit der Nachricht Hallo instanziiert

Ein Begrüßer-2 wird mit der Nachrichtenwelt instanziiert


2
Ist die Meldung für g2 korrekt? Geschieht das tatsächlich bei der Instanziierung oder tatsächlich nach der Instanziierung (selbst wenn es träge instanziiert wird)?
Tim Barrass
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.