“;def";”和“;val"定义一个函数

有什么区别:

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

而且

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

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

66663 次浏览

考虑一下:

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,即瓦尔,函数在声明时只初始化一次(因此它在第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

方法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指出的,你可以定义method,这是最快的选择:

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

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

看到这个:

  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 (even var)立即求值并赋值 现在将val改为def..它将打印9!Def是一个函数调用。

此外,Val是一个按值求值。这意味着右边的表达式在定义期间被求值。其中Def是通过名称求值。直到它被使用时才会计算。

瓦尔。“sq”在Scala定义中是固定的。它是在声明时计算的,以后不能更改。在其他例子中,even2也是val,但它声明了函数签名,即。"(Int => Boolean)",所以它不是Int类型。它是一个函数,它的值由下面的表达式设置

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

根据Scala的val属性,你不能将另一个函数赋值给even2,这与sq规则相同。

关于为什么调用eval2 val函数不打印“val”一次又一次?

源自代码:

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

我们知道,在Scala中,上述表达式的最后一条语句(在{..})实际上是返回到左边。所以你最终将even2设置为“x => x % 2 == 0”函数,这与你为even2声明的val类型匹配,即(Int => Boolean),所以编译器是快乐的。现在even2只指向“(x => x % 2 == 0)”函数(不是任何其他语句之前,即println("val")等。使用不同的参数调用event2实际上会调用“(x => x % 2 == 0)”代码,因为只有该代码与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
|             })
|        }

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

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


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


scala>

执行诸如def x = e这样的定义不会对表达式e求值。相反,每当x被调用时,e就会被求值。

或者,Scala提供了一个值定义 val x = e,作为定义求值的一部分,它对右边的值求值。 如果x随后被使用,它将立即被预先计算的e值所取代,这样表达式就不需要再次求值

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

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

在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,在初始化时求值

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

val函数的解释:

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

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

解释方法,即def:

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

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

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