有什么区别:
def even: Int => Boolean = _ % 2 == 0
和
val even: Int => Boolean = _ % 2 == 0
两者都可以像 even(10)
一样调用。
Int => Boolean
是什么意思?我认为定义语法是 def foo(bar: Baz): Bin = expr
方法 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)
),那么每次您将其用作函数时,编译器都会从您的方法中生成一个函数,因此性能可能会比使用 { 2}。
考虑一下:
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
even2
两次,一次使用 1
,一次使用 2
。每次通话都会得到不同的答案。因此,虽然 println
不会在后续调用中执行,但您不会从对 even2
的不同调用中获得相同的 result。至于为什么没有再次执行println
,这是一个不同的问题。
看到这个:
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 ie "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 值替换,因此不需要再次计算表达式。
此外,Val 是按值评估。这意味着在定义期间评估右侧表达式。其中 Def 是按名称评估。在使用之前不会进行评估。
除了上述有用的回复,我的发现是:
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 方法的调用。
解释方法即定义:
Scala 中的方法不是值,而是函数。
Scala 方法,就像在 Java 中一样,是类的一部分。它有一个名称、一个签名、可选的一些注释和一些字节码。
方法的实现是一个有序的语句序列,它产生一个必须与其返回类型兼容的值。
even
时评估函数不是很重要吗?def
可用于定义方法,这是最快的选项。 @A.卡里米even eq even
。@inline
attribute。但它不能内联函数,因为函数调用是对函数对象的虚拟apply
方法的调用。在某些情况下,JVM 可能会去虚拟化和内联此类调用,但一般情况下不会。