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

时间:2013-09-19 06:08:42

标签: scala

有什么区别:

def even: Int => Boolean = _ % 2 == 0

val even: Int => Boolean = _ % 2 == 0

两者都可以像even(10)一样调用。

9 个答案:

答案 0 :(得分:306)

方法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

使用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

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

请注意,还有第三个选项:lazy val

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

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

scala> even
scala.NotImplementedError: an implementation is missing

但每次都返回相同的结果(在本例中为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

<强>性能

val在定义时进行评估。

def会对每次通话进行评估,因此对于多个通话,性能可能会比val更差。只需一次通话即可获得相同的性能。如果没有通话,您将无法从def获得任何开销,因此即使您不在某些分支中使用它,也可以定义它。

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

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

def even(i: Int): Boolean = i % 2 == 0

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

答案 1 :(得分: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

你看到了区别吗?简而言之:

def :对于even的每次通话,它再次调用even方法的正文。但是使用even2 val ,函数在声明时仅初始化一次(因此它在第4行打印val并且再也没有)并且每次都使用相同的输出它被访问了。例如,尝试这样做:

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

初始化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

答案 2 :(得分: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)

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

答案 3 :(得分:1)

val ie&#34; sq&#34;是由Scala定义得到修复的。它在声明时进行评估,您无法在以后进行更改。在其他示例中,even2也是val,但是它用函数签名声明,即&#34;(Int =&gt; Boolean)&#34;,所以它不是Int类型。它是一个函数,它的值由以下表达式

设置
   {
         println("val");
         (x => x % 2 == 0)
   }

根据Scala val属性,您不能将另一个函数分配给even2,规则与sq相同。

关于为什么调用eval2 val函数不打印&#34; val&#34;一次又一次?

原始代码:

val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }

我们知道,在Scala中,上述类型表达式(内部{..})的最后一个声明实际上是返回到左侧。所以你最终将even2设置为&#34; x =&gt; x%2 == 0&#34;函数,它与你为even2 val类型声明的类型匹配,即(Int =&gt; Boolean),所以编译器很高兴。现在,even2仅指向&#34;(x =&gt; x%2 == 0)&#34;函数(不是任何其他语句,即println(&#34; val&#34;)等。调用具有不同参数的event2实际上会调用&#34;(x =&gt; x%2 == 0)&#34;代码,因为只有与event2一起保存。

scala> even2(2)
res7: Boolean = true

scala> even2(3)
res8: Boolean = false

为了澄清这一点,以下是不同版本的代码。

scala> val even2: (Int => Boolean) = {
     |              println("val");
     |              (x => { 
     |               println("inside final fn")
     |               x % 2 == 0
     |             })
     |        }

会发生什么?在这里我们看到&#34;在最后的fn&#34;当你打电话给even2()时,一次又一次地打印。

scala> even2(3)
inside final fn
res9: Boolean = false

scala> even2(2)
inside final fn
res10: Boolean = true

scala> 

答案 4 :(得分:1)

执行def x = e之类的定义不会评估表达式e。每当调用x时,都会评估。

或者,Scala提供值定义 val x = e,它评估右侧作为定义评估的一部分。 如果随后使用x,则立即将其替换为预先计算的e值,以便不需要再次计算表达式。

答案 5 :(得分:0)

另外,Val是一个按价值评估。这意味着在定义期间评估右侧表达式。其中Def是通过名称评估。在使用之前不会评估。

答案 6 :(得分:0)

除了上述有用的答复外,我的发现是:

def test1: Int => Int = {
x => x
}
--test1: test1[] => Int => Int

def test2(): Int => Int = {
x => x+1
}
--test2: test2[]() => Int => Int

def test3(): Int = 4
--test3: test3[]() => Int

上面显示“ def”是一种方法(参数参数为零),在调用时会返回另一个函数“ Int => Int”。

方法到函数的转换在这里有很好的解释:https://tpolecat.github.io/2014/06/09/methods-functions.html

答案 7 :(得分:0)

在REPL中,

scala> def even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean

scala> val even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean = $$Lambda$1157/1017502292@57a0aeb8

def表示call-by-name,按需评估

val表示call-by-value,在初始化时求值

答案 8 :(得分:0)

注意:Scala 中有不同类型的函数:抽象的、具体的、匿名的、高阶的、纯的、不纯的等等...

解释val函数:

Scala 中的 val 函数是一个完整的对象。 Scala 中有一些特征来表示具有不同数量参数的函数:Function0、Function1、Function2 等。作为实现这些特征之一的类的实例,函数对象具有方法。其中一种方法是 apply 方法,它包含实现函数体的代码。

当我们创建一个值为函数对象的变量,然后我们引用该变量后跟括号时,这将转换为对函数对象的 apply 方法的调用。

解释方法即def:

Scala 中的方法不是值,而是函数。

Scala 方法,就像在 Java 中一样,是类的一部分。它有一个名称、一个签名、一些可选的注释和一些字节码。

方法的实现是一个有序的语句序列,它产生一个必须与其返回类型兼容的值。