【问题标题】:Differences between these three ways of defining a function in ScalaScala中这三种定义函数的方式的区别
【发布时间】:2010-09-05 16:14:36
【问题描述】:

给定三种表达相同功能的方式f(a) := a + 1

val f1 = (a:Int) => a + 1
def f2 = (a:Int) => a + 1
def f3:(Int => Int) = a => a + 1

这些定义有何不同? REPL 没有指出任何明显的差异:

scala> f1
res38: (Int) => Int = <function1>
scala> f2
res39: (Int) => Int = <function1>
scala> f3
res40: (Int) => Int = <function1>

【问题讨论】:

  • 您应该注意,在上面的第二块中,在 REPL 中计算 f1 显示静态绑定到 f1 的值,同时计算 f2f3 显示 的结果调用这些方法。特别是,每次调用f2f3 时都会生成一个新的Function1[Int, Int] 实例,而f1 永远是相同的Function1[Int, Int]
  • @RandallSchulz 鉴于 val 版本不需要新的函数实例,为什么要在这种情况下使用 def?
  • @virtualeyes 我能回忆起的唯一情况是在组合解析器库中看到 defs 产生 FunctionN[...] 值。编写产生函数的方法并不常见,而且几乎永远不会使用 def 产生语义/功能不变函数的多个副本。

标签: scala


【解决方案1】:

在类中,val 在初始化时评估,而 def 仅在并且每次调用函数时评估。在下面的代码中,您将看到 x 在第一次使用对象时被评估,但在访问 x 成员时不会再次被评估。相反,在实例化对象时不评估 y,而是在每次访问成员时评估。

  class A(a: Int) {
    val x = { println("x is set to something"); a }
    def y = { println("y is set to something"); a }
  }

  // Prints: x is set to something
  val a = new A(1)

  // Prints: "1"
  println(a.x)

  // Prints: "1"                               
  println(a.x)

  // Prints: "y is set to something" and "1"                                  
  println(a.y)

  // Prints: "y is set to something" and "1"                                                                                   
  println(a.y)

【讨论】:

  • @JacobusR 仅在类中是这样吗?
  • 例如:scala> var b = 5 b: Int = 5 scala> val a: (Int => Int) = x => x + b a: Int => Int = scala > a(5) res48: Int = 10 scala> b = 6 b: Int = 6 scala> a(5) res49: Int = 11 我期待 a(5) 返回 10 并且 b 的值已被内联
  • @AndrewCassidy 函数a 是不可变的并且在初始化时进行评估,但b 仍然是一个可变值。所以对b 的引用是在初始化期间设置的,但b 存储的值仍然是可变的。为了好玩,您现在可以创建一个新的val b = 123。在此之后,您的 a(5) 将始终给出 11,因为 b 现在是一个全新的值。
  • @JacobusR 谢谢……这是有道理的。这与“词法范围”的定义相吻合,因为函数 a 带有对原始“var b”的引用。我想让我感到困惑的是: var b = 5; val c = b; b = 6;行为不同。我想我不应该期望一个包含对原始“词法”范围的引用的函数定义与 Int 的行为方式相同。
【解决方案2】:

f1 是一个接受整数并返回整数的函数。

f2 是一个零元数的方法,它返回一个接受整数并返回整数的函数。 (当你稍后在 REPL 中输入 f2 时,它变成了对方法 f2 的调用。)

f3f2 相同。你只是没有在那里使用类型推断。

【讨论】:

  • 为什么f1functionf2method
  • @Freewind,函数是一个对象,其方法名为apply。方法,嗯,就是方法。
  • 很棒的答案。问题:你说 f2 的元数为零,但它不是一元的吗? en.wikipedia.org/wiki/Arity "一元函数不带参数。一元函数只带一个参数。"只是好奇!
  • @MatthewCornell, f2 本身不接受任何参数。它返回的函数对象确实如此。
【解决方案3】:

执行诸如 def x = e 之类的定义不会计算表达式 e。而是在使用 x 时评估 e。或者,Scala 提供了一个值定义 val x = e,它确实评估右侧的 e 作为评估的一部分 的定义。如果随后使用 x,则立即将其替换为 e 的预计算值,因此不需要再次计算表达式。

Scala 示例 马丁·奥德斯基

【讨论】:

    猜你喜欢
    • 2011-06-27
    • 2018-11-07
    • 2015-12-26
    • 1970-01-01
    • 2011-03-15
    • 1970-01-01
    • 2012-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多