Qual è la funzione di applicazione in Scala?


311

Non l'ho mai capito dai sostantivi unmarshalling e verbing (una AddTwoclasse ne ha uno applyche ne aggiunge due!).

Capisco che è zucchero sintattico, quindi (ho dedotto dal contesto) deve essere stato progettato per rendere un po 'di codice più intuitivo.

Che significato applydà una classe con una funzione? A cosa serve e a quali scopi migliora il codice (smascheramento, nomi verbali ecc.)?

come aiuta se usato in un oggetto compagno?


4
Anche una rapida ricerca su google porta molti articoli carini. Eccone uno: jackcoughonsoftware.blogspot.com/2009/01/…
om-nom-nom


6
In realtà è lo stesso di un costruttore in Java / C ++, ma può restituire il valore e ha 'applicare' nome
SES

È un metodo, non una funzione. La distinzione tra metodi e funzioni è molto importante in Scala.
destra

1
Per approfondire Zoidberg, "metodi sono solo funzioni che possono accedere allo stato della classe" twitter.github.io/scala_school/basics.html In generale, questo vale per più di Scala stackoverflow.com/questions/155609/...
DharmaTurtle

Risposte:


641

I matematici hanno i loro piccoli modi divertenti, quindi invece di dire "allora chiamiamo funzione fpassandola xcome parametro", come direbbero i programmatori, parlano di "applicare la funzione fal suo argomento x".

In matematica e informatica, Apply è una funzione che applica funzioni agli argomenti.
Wikipedia

applyserve allo scopo di colmare il divario tra paradigmi orientati agli oggetti e funzionali in Scala. Ogni funzione in Scala può essere rappresentata come un oggetto. Ogni funzione ha anche un tipo OO: ad esempio, una funzione che accetta un Intparametro e restituisce un Inttipo OO avrà 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

Dato che tutto è un oggetto in Scala fora può essere trattato come un riferimento Function1[Int,Int]all'oggetto. Ad esempio, possiamo chiamare il toStringmetodo ereditato da Any, che sarebbe stato impossibile per una funzione pura, perché le funzioni non hanno metodi:

  f.toString

Oppure potremmo definire un altro Function1[Int,Int]oggetto chiamando il composemetodo fe concatenando due diverse funzioni insieme:

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

Ora se vogliamo effettivamente eseguire la funzione, o come dice il matematico "applica una funzione ai suoi argomenti", chiameremmo il applymetodo Function1[Int,Int]sull'oggetto:

 f2.apply(2)

Scrivere f.apply(args)ogni volta che si desidera eseguire una funzione rappresentata come oggetto è orientato agli oggetti, ma aggiungerebbe un sacco di disordine al codice senza aggiungere molte informazioni aggiuntive e sarebbe bello poter usare più notazioni standard, come come f(args). È qui che interviene il compilatore Scala e ogni volta che abbiamo un riferimento fa un oggetto funzione e scriviamo f (args)per applicare argomenti alla funzione rappresentata, il compilatore si espande silenziosamente f (args)alla chiamata del metodo oggetto f.apply (args).

Ogni funzione in Scala può essere trattata come un oggetto e funziona anche nell'altro modo - ogni oggetto può essere trattato come una funzione, purché abbia il applymetodo. Tali oggetti possono essere utilizzati nella notazione della funzione:

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

Ci sono molti casi d'uso in cui vorremmo trattare un oggetto come una funzione. Lo scenario più comune è un modello di fabbrica . Invece di aggiungere disordine al codice utilizzando un metodo factory, possiamo applyobiettare a una serie di argomenti per creare una nuova istanza di una classe associata:

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) 

Quindi il applymetodo è solo un modo pratico per colmare il divario tra funzioni e oggetti in Scala.


1
come aggiungeresti un tipo di variabile personalizzato a un oggetto. AFAIK non è possibile. Ecco un esempio: hai questa classe class Average[YType](yZeroValue:YType). Come si passa YType dal suo oggetto poiché gli oggetti non possono accettare i parametri di tipo?
Adrian,

I tipi in Scala di solito possono essere dedotti in base agli argomenti, ma in caso contrario è possibile fornirli tramite le parentesi quadre. quindi quando crei un'istanza di un oggetto medio potresti dire "val avg = new Average [Int] (0)"
Angelo Genovese,

4
Le classi di casi meritano di essere menzionate qui. Le classi di case aggiungono applye unapplymetodi in modo conveniente, automatico e invisibile a un oggetto compagno della classe (tra le altre cose). Quindi la definizione di una classe con una sola riga in questo modo case class Car(make:String, model: String, year: Short, color: String)rende possibile la produzione di nuovi oggetti della classe Car semplicemente: val myCar = Car("VW","Golf",1999,"Blue"). Questa è una scorciatoia perCar.apply(...)
Kjetil S.

1
every object can be treated as a function, provided it has the apply method- così tanto ora ha senso.
Arj,


51

Viene dall'idea che spesso si desidera applicare qualcosa a un oggetto. L'esempio più accurato è quello delle fabbriche. Quando si dispone di una fabbrica, si desidera applicare un parametro per creare un oggetto.

I ragazzi di Scala hanno pensato che, come accade in molte situazioni, potrebbe essere bello avere una scorciatoia da chiamare apply. Pertanto, quando si danno parametri direttamente a un oggetto, viene desugared come se si passassero questi parametri alla funzione di applicazione di quell'oggetto:

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

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

Viene spesso utilizzato nell'oggetto companion, per fornire un metodo di fabbrica piacevole per una classe o un tratto, ecco un esempio:

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

Dalla libreria scala standard, puoi vedere come scala.collection.Seqviene implementato: Seqè un tratto, quindi new Seq(1, 2)non verrà compilato ma grazie all'oggetto compagno e all'applicazione, puoi chiamare Seq(1, 2)e l'implementazione viene scelta dall'oggetto compagno.


L'equivalente di applicare potrebbe essere realizzato anche usando Implicits?
jayunit100,

11

Ecco un piccolo esempio per coloro che vogliono esaminare rapidamente

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


} 

produzione

Un greeter-1 viene istanziato con il messaggio hello

Un greeter-2 viene istanziato con il mondo dei messaggi


2
Il messaggio per g2 è corretto? Sta accadendo realmente all'istanza, o in realtà dopo l'istanza (anche se è pigramente istanziata)?
Tim Barrass,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.