Qual é a função aplicar no Scala?


311

Eu nunca entendi a partir dos substantivos não ordenados e verbais (uma AddTwoclasse tem umapply exemplos que adiciona dois!).

Entendo que é um açúcar sintático, então (deduzi do contexto) ele deve ter sido projetado para tornar algum código mais intuitivo.

Qual o significado de uma classe com uma applyfunção? Para que é usado e para quais propósitos ele melhora o código (remoção da correspondência, verbos verbais etc.)?

como isso ajuda quando usado em um objeto complementar?


4
Até mesmo uma pesquisa rápida traz muitos artigos legais. Aqui está um: jackcoughonsoftware.blogspot.com/2009/01/…
om-nom-nom


6
na verdade, é o mesmo que um construtor em Java / C ++, mas pode retornar o valor e tem 'apply' name
ses

É um método, não uma função. A distinção entre métodos e funções é muito importante em Scala.
rightfold

1
Para elaborar o Zoidberg, "Métodos são apenas funções que podem acessar o estado da classe" twitter.github.io/scala_school/basics.html Em geral, isso se aplica a mais de Scala stackoverflow.com/questions/155609/…
DharmaTurtle

Respostas:


641

Os matemáticos têm suas próprias maneiras engraçadas, então, em vez de dizer "então chamamos a função fpassando xcomo parâmetro", como diríamos os programadores, eles falam sobre "aplicar a função fao seu argumento x".

Em matemática e ciência da computação, Apply é uma função que aplica funções a argumentos.
Wikipedia

applyserve ao propósito de fechar a lacuna entre paradigmas orientados a objetos e funcionais em Scala. Toda função no Scala pode ser representada como um objeto. Toda função também possui um tipo OO: por exemplo, uma função que pega um Intparâmetro e retorna um Intterá o tipo 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

Como tudo é um objeto no Scala fagora pode ser tratado como uma referência ao Function1[Int,Int]objeto. Por exemplo, podemos chamar o toStringmétodo herdado de Any, que seria impossível para uma função pura, porque funções não possuem métodos:

  f.toString

Ou podemos definir outro Function1[Int,Int]objeto chamando o composemétodo fe encadeando duas funções diferentes:

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

Agora, se queremos realmente executar a função, ou, como matemático diz "aplicar uma função aos seus argumentos", chamaríamos o applymétodo no Function1[Int,Int]objeto:

 f2.apply(2)

Escrever f.apply(args)toda vez que você deseja executar uma função representada como um objeto é a maneira Orientada a Objetos, mas adicionaria muita confusão ao código sem adicionar muita informação adicional e seria bom poder usar uma notação mais padrão, como como f(args). É aí que o compilador Scala intervém e sempre que temos uma referência fa um objeto de função e escrevemos f (args)para aplicar argumentos à função representada, o compilador se expande silenciosamente f (args)para a chamada do método de objeto f.apply (args).

Toda função no Scala pode ser tratada como um objeto e funciona da outra maneira - todo objeto pode ser tratado como uma função, desde que tenha o applymétodo. Tais objetos podem ser usados ​​na notação de função:

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

Existem muitos casos de uso em que queremos tratar um objeto como uma função. O cenário mais comum é um padrão de fábrica . Em vez de adicionar confusão ao código usando um método factory, podemos applyobjetar um conjunto de argumentos para criar uma nova instância de uma classe associada:

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) 

Portanto, o applymétodo é apenas uma maneira útil de fechar a lacuna entre funções e objetos no Scala.


1
como você adicionaria um tipo de variável personalizado a um objeto. AFAIK não é possível. Aqui está um exemplo: você tem esta classe class Average[YType](yZeroValue:YType). Como você passa o YType de seu objeto, uma vez que os objetos não podem receber parâmetros de tipo?
267 Adrian

Os tipos no Scala geralmente podem ser inferidos com base nos argumentos, mas, se não, você pode fornecê-los através de colchetes. por isso, quando instanciar um objeto média você poderia dizer "val avg = new Média [Int] (0)"
Angelo Genovese

4
As classes de casos merecem uma menção aqui. As classes de caso adicionam de forma conveniente, automática e invisível applye unapplymétodos a um objeto complementar da classe (entre outras coisas). Assim, definindo uma classe com apenas uma linha como esta case class Car(make:String, model: String, year: Short, color: String)faz com que seja possível produzir novos objetos da classe Car por apenas: val myCar = Car("VW","Golf",1999,"Blue"). Isto é uma abreviação paraCar.apply(...)
Kjetil S.

1
every object can be treated as a function, provided it has the apply method- tanto agora faz sentido.
Arj


51

Vem da ideia de que você frequentemente deseja aplicar algo a um objeto. O exemplo mais preciso é o das fábricas. Quando você tem uma fábrica, deseja aplicar parâmetro a ele para criar um objeto.

Os caras do Scala pensaram que, como ocorre em muitas situações, pode ser bom ter um atalho para chamar apply. Portanto, quando você fornece parâmetros diretamente a um objeto, é sugerido como se você passasse esses parâmetros para a função de aplicação desse objeto:

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

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

Geralmente é usado no objeto complementar, para fornecer um bom método de fábrica para uma classe ou característica, eis um exemplo:

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

Na biblioteca padrão da scala, você pode ver como scala.collection.Seqé implementado: Seqé uma característica, portanto new Seq(1, 2), não será compilada, mas graças ao objeto complementar e à aplicação, você pode chamar Seq(1, 2)e a implementação é escolhida pelo objeto complementar.


O equivalente a aplicar também pode ser realizado com implícitos?
precisa saber é o seguinte

11

Aqui está um pequeno exemplo para quem quer ler 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")


} 

resultado

Um greeter-1 está sendo instanciado com a mensagem hello

Um greeter-2 está sendo instanciado com o mundo das mensagens


2
A mensagem para g2 está correta? Isso está realmente acontecendo na instanciação, ou é de fato após a instanciação (mesmo que seja instanciada preguiçosamente)?
Tim Barrass
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.