scala 定义一个函数的“def”和“瓦尔”有什么区别

ybzsozfc  于 2023-05-07  发布在  Scala
关注(0)|答案(9)|浏览(256)

以下两者之间的区别是什么:

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

和/或

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

两者都可以称为even(10)

jslywgbw

jslywgbw1#

方法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稍差。
使用java 8+ lambda优化,将方法转换为函数是一个廉价的操作。

kiayqfof

kiayqfof2#

想想这个:

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,即瓦尔,该函数在while声明中只初始化一次(因此它在第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。即,第一次使用它时,它被初始化,而不是while声明。例如:

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

scala> y
res4: Int = 323930673

scala> y
res5: Int = 323930673
qvk1mo1f

qvk1mo1f3#

看这个:

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!瓦尔(even var)会立即求值并赋值。
现在将瓦尔改为def。它将打印9!Def是函数调用。它将在每次被调用时进行评估。

mccptt67

mccptt674#

瓦尔,即Scala定义的“sq”是固定的。它在声明的时候就被计算,你不能在以后修改。在其他示例中,其中even 2也是瓦尔,但它用函数签名声明,即“(Int =〉Boolean)",所以它不是Int类型。它是一个函数,其值由以下表达式设置

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

根据Scala瓦尔属性,你不能将另一个函数赋值给even 2,这和sq的规则相同。
关于为什么调用eval 2 val函数一次又一次不打印“val”?
原始代码:

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

我们知道,在Scala中,上述类型表达式的最后一条语句(在{..})实际上是返回到左手边。因此,您最终将even 2设置为“x =〉x % 2 == 0”函数,该函数与您为even 2瓦尔类型声明的类型相匹配,即(Int =〉Boolean),所以编译器很高兴。现在even 2只指向“(x =〉x % 2 == 0)”函数(之前没有任何其他语句,即println(“瓦尔”)等。使用不同的参数调用event 2实际上会调用“(x =〉x % 2 == 0)”代码,因为只有它是用event 2保存的。

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

会发生什么?在这里,当您调用even 2()时,我们看到“inside final fn”一次又一次地打印出来。

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

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

scala>
p8ekf7hl

p8ekf7hl5#

执行def x = e这样的定义不会计算表达式e。相反,每当调用x时,e都被求值。
或者,Scala提供了一个值定义val x = e,它会在定义的求值过程中对右侧进行求值。如果随后使用x,则它立即被预先计算的e的值替换,使得表达式不需要再次求值。

bnl4lu3b

bnl4lu3b6#

此外,瓦尔是一个按值评估。这意味着右边的表达式在定义期间进行计算。其中Def是按名称评估。它在使用之前不会求值。

ymdaylpp

ymdaylpp7#

除了以上有用的答复外,我的研究结果如下:

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

yuvru6vn

yuvru6vn8#

在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,按需计算
瓦尔表示call-by-value,在初始化时计算

zzlelutf

zzlelutf9#

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

瓦尔函数说明:

Scala中的瓦尔函数是一个完整的对象。Scala中有一些traits来表示具有不同数量参数的函数:函数0、函数1、函数2等。作为实现这些特性之一的类的示例,函数对象具有方法。其中一个方法是apply方法,它包含实现函数体的代码。
当我们创建一个变量,其值是一个函数对象,然后引用该变量,后面跟着括号,这将转换为对函数对象的apply方法的调用。

解释方法即def:

Scala中的方法不是值,而是函数。
在Java中,Scala方法是类的一部分。它有一个名称、一个签名、可选的一些注解和一些字节码。
方法的实现是一个有序的语句序列,它产生的值必须与其返回类型兼容。

相关问题