定义函数的"def"和"val"之间有什么区别

Ami*_*imi 205 scala

有什么区别:

def even: Int => Boolean = _ % 2 == 0
Run Code Online (Sandbox Code Playgroud)

val even: Int => Boolean = _ % 2 == 0
Run Code Online (Sandbox Code Playgroud)

两者都可以称为even(10).

sen*_*nia 316

方法def even在调用时进行评估并每次创建新函数(新实例Function1).

def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false

val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
Run Code Online (Sandbox Code Playgroud)

有了def你可以在每次调用新的功能:

val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1049057402
test()
// Int = -1049057402 - same result

def test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -240885810
test()
// Int = -1002157461 - new result
Run Code Online (Sandbox Code Playgroud)

val在定义def时评估,- 当被调用时:

scala> val even: Int => Boolean = ???
scala.NotImplementedError: an implementation is missing

scala> def even: Int => Boolean = ???
even: Int => Boolean

scala> even
scala.NotImplementedError: an implementation is missing
Run Code Online (Sandbox Code Playgroud)

请注意,还有第三种选择:lazy val.

它在第一次调用时进行评估:

scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>

scala> even
scala.NotImplementedError: an implementation is missing
Run Code Online (Sandbox Code Playgroud)

FunctionN每次都返回相同的结果(在本例中为相同的实例):

lazy val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true

lazy val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1068569869
test()
// Int = -1068569869 - same result
Run Code Online (Sandbox Code Playgroud)

性能

val 评估何时定义.

def评估每个呼叫,因此性能可能比val多个呼叫更差.只需一次通话即可获得相同的性能.没有任何调用你就不会有任何开销def,所以你可以定义它,即使你不在某些分支中使用它.

有了lazy val你会得到一个懒惰的评价:你可以定义它,即使你不会在某些分支中使用它,它会评估一次或从不,但你会得到一点点开销,双重检查锁定每次访问你的lazy val.

正如@SargeBorsch指出你可以定义方法,这是最快的选择:

def even(i: Int): Boolean = i % 2 == 0
Run Code Online (Sandbox Code Playgroud)

但是如果你需要一个函数(不是方法)用于函数组合或更高阶函数(比如filter(even)),编译器会在你每次将它作为函数使用时从你的方法生成一个函数,所以性能可能会稍微差一些val.

  • `def`可用于定义方法,这是最快的选择.@ A.Karimi (2认同)
  • @ A.Karimi:看看更新.一般来说,你应该使用`val`. (2认同)
  • 为了好玩:在2.12,`甚至eq甚至`. (2认同)
  • @animageofmine Scala编译器可以尝试内联方法.这里有[`@inline`属性](https://www.scala-lang.org/api/current/scala/inline.html).但是它不能内联函数,因为函数调用是对函数对象的虚拟`apply`方法的调用.在某些情况下,JVM可能会虚拟化并内联此类调用,但通常不会. (2认同)

Jat*_*tin 23

考虑一下:

scala> def even: (Int => Boolean) = {
             println("def"); 
             (x => x % 2 == 0)
       }
even: Int => Boolean

scala> val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }
val //gets printed while declaration. line-4
even2: Int => Boolean = <function1>

scala> even(1)
def
res9: Boolean = false

scala> even2(1)
res10: Boolean = false
Run Code Online (Sandbox Code Playgroud)

你看得到差别吗?简而言之:

def:对于每次调用even,它even再次调用方法的主体.但是使用even2ie val,函数在声明时只初始化一次(因此它val在第4行打印而且从不再打印)并且每次访问时都使用相同的输出.例如,尝试这样做:

scala> import scala.util.Random
import scala.util.Random

scala> val x = { Random.nextInt }
x: Int = -1307706866

scala> x
res0: Int = -1307706866

scala> x
res1: Int = -1307706866
Run Code Online (Sandbox Code Playgroud)

x初始化时,返回的值Random.nextInt设置为最终值x.下次x再次使用时,它将始终返回相同的值.

你也可以懒洋洋地初始化x.即第一次使用它时它被初始化而不是在声明时.例如:

scala> lazy val y = { Random.nextInt }
y: Int = <lazy>

scala> y
res4: Int = 323930673

scala> y
res5: Int = 323930673
Run Code Online (Sandbox Code Playgroud)

  • 我认为你的解释可能意味着你不想要的东西.尝试两次调用`even2`,一次调用`1`,一次调用`2`.每次通话都会得到不同的答案.因此,虽然`println`没有在后续调用中执行,但是从不同的`even2`调用中得不到相同的*result*.至于为什么`println`不再被执行,这是一个不同的问题. (5认同)

Apu*_*ngh 5

看到这个:

  var x = 2 // using var as I need to change it to 3 later
  val sq = x*x // evaluates right now
  x = 3 // no effect! sq is already evaluated
  println(sq)
Run Code Online (Sandbox Code Playgroud)

令人惊讶的是,它将打印4而不是9!val(偶数var)将立即求值并赋值。
现在将val更改为def ..它将打印9!Def是一个函数调用。它将在每次调用时求值。