有什么区别:
def even: Int => Boolean = _ % 2 == 0
和
val even: Int => Boolean = _ % 2 == 0
两者都可以称为 like even(10)
。
有什么区别:
def even: Int => Boolean = _ % 2 == 0
和
val even: Int => Boolean = _ % 2 == 0
两者都可以称为 like even(10)
。
方法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
,因此即使您不会在某些分支中使用它,您也可以定义它。
使用 alazy val
你会得到一个惰性评估:即使你不会在某些分支中使用它,你也可以定义它,并且它评估一次或从不评估,但是你会从每次访问你的lazy val
.
正如@SargeBorsch 所说,您可以定义方法,这是最快的选择:
def even(i: Int): Boolean = i % 2 == 0
但是,如果您需要一个函数(而不是方法)用于函数组合或更高阶函数(如filter(even)
),每次您将其用作函数时,编译器都会从您的方法中生成一个函数,因此性能可能比 with 稍差val
。
考虑一下:
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,该函数在声明时仅初始化一次(因此它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
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
看到这个:
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 中一样,是类的一部分。它有一个名称、一个签名、可选的一些注释和一些字节码。
方法的实现是一个有序的语句序列,它产生一个必须与其返回类型兼容的值。