之间有什么区别?
def even: Int => Boolean = _ % 2 == 0
和
val even: Int => Boolean = _ % 2 == 0
两者都可以称为even(10)
。
之间有什么区别?
def even: Int => Boolean = _ % 2 == 0
和
val even: Int => Boolean = _ % 2 == 0
两者都可以称为even(10)
。
Answers:
方法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
。
even
调用时评估函数不是很重要。
def
可用于定义方法,这是最快的选择。@ A.Karimi
even eq even
。
考虑一下:
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
ie 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
even2
两次,一次致电一次,一次致电1
一次 2
。您将在每个电话中得到不同的答案。因此,虽然println
在后续调用中未执行,但您从调用的不同不会得到相同的结果even2
。至于为什么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(偶数var)将立即求值并赋值。
现在将val更改为def.。它将打印9!Def是一个函数调用..它将在每次调用时求值。
val即“ 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
,在初始化时求值
Int => Boolean
意思?我认为定义语法是def foo(bar: Baz): Bin = expr