【问题标题】:What is the apply function in Scala?Scala 中的 apply 函数是什么?
【发布时间】:2012-04-02 00:01:43
【问题描述】:

我从来没有从人为的 unmarshalling 和动词名词(AddTwo 类有一个 apply 增加两个!)示例中理解它。

我知道它是语法糖,所以(我从上下文推断)它一定是为了让一些代码更直观。

带有apply 函数的类有什么意义?它的用途是什么,它使代码变得更好(解组、动词名词等)的目的是什么?

在伴随对象中使用时有什么帮助?

【问题讨论】:

标签: scala


【解决方案1】:

数学家有他们自己有趣的小方法,所以不像我们程序员所说的那样说“然后我们调用函数f 传递它x 作为参数”,他们谈论的是“将函数f 应用于它的参数x"。

在数学和计算机科学中,Apply 是一个应用函数 函数到参数。
Wikipedia

apply 的目的是缩小 Scala 中面向对象和函数式范式之间的差距。 Scala 中的每个函数都可以表示为一个对象。每个函数也有一个 OO 类型:例如,一个接受 Int 参数并返回 Int 的函数将具有 Function1[Int,Int] 的 OO 类型。

 // 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] 对象的引用。例如,我们可以调用继承自AnytoString 方法,这对于纯函数来说是不可能的,因为函数没有方法:

  f.toString

或者我们可以通过在f 上调用compose 方法并将两个不同的函数链接在一起来定义另一个Function1[Int,Int] 对象:

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

现在,如果我们想实际执行函数,或者像数学家所说的“将函数应用于其参数”,我们将在 Function1[Int,Int] 对象上调用 apply 方法:

 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 中函数和对象之间差距的一种便捷方式。

【讨论】:

  • 如何将自定义变量类型添加到对象。 AFAIK 这是不可能的。这是一个例子:你有这个类class Average[YType](yZeroValue:YType)。由于对象不能接受类型参数,你如何从它的对象中传递 YType?
  • Scala 中的类型通常可以根据参数推断出来,但如果不是,您可以通过方括号提供它们。所以当实例化一个 Average 对象时,你可以说“val avg = new Average[Int](0)”
  • 案例类在这里值得一提。案例类方便、自动且不可见地将applyunapply 方法添加到类的伴随对象(除其他外)。因此,只用这样的一行 case class Car(make:String, model: String, year: Short, color: String) 定义一个类,就可以通过 val myCar = Car("VW","Golf",1999,"Blue") 生成 Car 类的新对象。这是Car.apply(...) 的简写
  • every object can be treated as a function, provided it has the apply method - 现在说的太多了。
【解决方案2】:

它源于您经常想要应用某些东西到一个对象的想法。更准确的例子是工厂之一。当你有一个工厂时,你想应用参数给它创建一个对象。

Scala 的人认为,由于它在很多情况下都会发生,所以最好有一个快捷方式来调用 apply。因此,当您将参数直接传递给对象时,就好像您将这些参数传递给该对象的 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 标准库中,您可能会看到 scala.collection.Seq 是如何实现的:Seq 是一个特征,因此 new Seq(1, 2) 不会编译,但由于伴随对象和应用,您可以调用 Seq(1, 2) 和实现由伴随对象选择。

【讨论】:

  • 是否也可以使用 Implicits 来完成 apply 的等效操作?
【解决方案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")


} 

输出

greeter-1 正在用消息 hello 实例化

greeter-2 正在用消息世界实例化

【讨论】:

  • g2 的消息是否正确?这实际上是在实例化时发生的,还是实际上是在实例化之后发生的(即使它是延迟实例化的)?
【解决方案4】:

来自c++的人的TLDR

它只是( )括号的重载运算符

所以在 scala 中:

class X {
   def apply(param1: Int, param2: Int, param3: Int) : Int = {
     // Do something
   }
}

和c++中的这个一样:

class X {
   int operator()(int param1, int param2, int param3) {
      // do something
   }
};

【讨论】:

    【解决方案5】:

    1 - 将函数视为对象。

    2 - apply 方法类似于 Python 中的 __call __,它允许您将给定类的实例用作函数。

    【讨论】:

      猜你喜欢
      • 2011-04-17
      • 2020-01-04
      • 2017-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-21
      相关资源
      最近更新 更多