def是否被认为是Scala中的一个闭包?

时间:2017-08-19 12:43:33

标签: scala function closures

我想理解声明为val的闭包和声明为method的闭包之间的区别(使用def kyeword)。究竟这两个函数之间的区别是ep方法是一个闭包吗?

scala> var more = 10

scala> val phi =(x:Int)=> x + more

scala> def ep(x:Int):Int = x + more

2 个答案:

答案 0 :(得分:2)

捕获自由变量的函数对象,据说是 “关闭”对创建时可见的变量称为闭包。例如:

var foo = 10
val bar = (x:Int) => x+foo

此处,foo在函数文字中是自由变量,因此函数文字是开放术语。因此,val bar闭包,因为函数值(对象)是使用开放式术语foo创建的。

现在,让我们从功能定义方面的 val def 开始。

  

在scala中,function是一个值,您可以将其赋值给变量。

当您使用 val 定义函数时,您使用函数文字val bar = (x:Int) => x+foo定义它,返回一个函数对象并将其指定为值到bar。在 val 的情况下,在定义时评估函数值。这意味着,函数文字(x:Int) => x+foo将使用自由变量foo计算为函数值,并作为闭包存储在bar变量中。

//In the following snippet, a function literal is evaluated to object Function1 and assigned to val bar. 
scala> var foo = 10
foo: Int = 10

scala> val bar = (x: Int) => x + foo
bar: Int => Int = <function1>         //Function1 object is returned

因为bar是一个值并且在定义时被计算,所以无论何时访问它都将始终引用内存中的同一个对象。

scala> bar eq bar       //because both instance refer to same instance. 
res11: Boolean = true

另一方面,您使用 def 定义方法方法不是Scala中的函数。根据Scala语言规范,method do not have type in Scala hence it cannot be used as value。 例如,你不能这样做:

val bar = {def foo(x:Int): Int = x + 1 }

但是,根据规范,如果方法名称用作值,则Scala 隐式转换为具有ETA Expression的相应函数类型。例如方法def a: Int转换为a: => Int。这意味着,每次调用方法时,都会返回一个函数值

scala> def foo():Int = 1
a: ()Int

scala> val bar:( ()=> Int) = foo
bar: () => Int = <function0>        //foo return Function0

//Note, val bar = foo, will assign the value returned by foo instead of returning function, so specify type. 

因此, 方法可用作功能 。例如,当某些方法或函数需要函数类型作为参数时,您可以提供def方法。

scala> val foo = (x:Int, double:Function1[Int, Int]) => double(x)
foo: (Int, Int => Int) => Int = <function2>

scala> def double(x:Int):Int = x * 2
double: (x: Int)Int

scala> foo(3, double)
res1: Int = 6

另外,请注意,使用方法,您可以在每次通话时获得新功能。

scala> def double: Int => Int = _ * 2
double: Int => Int

scala> double eq double
res15: Boolean = false      //false because every time double refers to new instance and hence is not equal unlike val. 

现在,我们来关闭。 使用 val def 方法定义的函数文字都返回函数值(对象)。从函数文字在运行时创建的函数值(对象)是闭包。另一方面,方法不是闭包,但是通过调用方法得到的函数值是闭包。

scala> var more = 1
more: Int = 1

scala> val foo = (x:Int) => x + more    // foo is clouser.
foo: Int => Int = <function1>

scala> def bar(x:Int):Int = x + more    // bar is not clouser.
bar: (x: Int)Int

scala> val barClouser : (Int => Int) = bar  // barClouser is clouser. 
barClouser: Int => Int = <function1>

scala> val barValue = bar(3)    // bar is converted to clouser and then clouser value is evaluated and result is assigned to barValue variable. 
barValue: Int = 4   

scala> val fooValue = foo(3)    //foo value is evaluated and returned as value which is assigned to fooValue variable. 
fooValue: Int = 4

答案 1 :(得分:1)

即使在closure互联网上有一些定义,如果该函数捕获自由变量,那么它就是闭包,Martin Odersky's scala中 closure 的定义

  

一个捕获自由变量的函数对象,据说它在创建时可见的变量上“关闭”。 - 奥德斯基,马丁;勺子,Lex;通票,比尔。 Scala编程:全面的循序渐进指南

由于def不是函数对象,因为它不是内存中的对象,我会说ep即使它捕获了一个自由变量也不是闭包。

函数文字和方法之间的差异:

声明函数文字时:

val phi= (x:Int)=>x+more

在运行时,您可以创建类型为Function1[Int, Int]的变量。它变成一个函数值,即变量在内存(堆)中占用空间。

声明函数声明时:

def ep(x:Int):Int= x+more

您在该类的虚拟方法表中创建一个新条目,而不占用堆中的空间,并且不需要GC来清理内容。

此外def可以使用val(函数值)不能的类型参数。

修改

阅读有关功能值定义的Glossary

  

功能值   一个可以像任何其他函数一样调用的函数对象。函数值的类从包scala中扩展FunctionN特征之一(例如,Function0,Function1),并且通常通过函数文字语法在源代码中表示。调用其apply方法时,将“调用”函数值。捕获自由变量的函数值是一个闭包。

并且考虑到函数对象的上述定义,我们可以得出结论,定义为def的方法不是闭包,因为它没有扩展任何FunctionN特征,也没有定义为函数文字语法。

创建def不会创建闭包,证明:

scala> def x: Int = 3
x: Int

与之相反:

scala> val z: () => Int = () => 3
z: () => Int = $$Lambda$1044/229318537@3a17b2e3

您可以看到创建def并没有像使用函数文字语法那样吐出内存引用。