我对函数式编程概念很陌生,正在观看Neil Ford在youtube中的演讲。在那里,他谈到了一个计数器来演示一段不使用全局状态的代码(在20:04)。来自Java世界,我很难理解这里的概念以及计数器如何递增。以下是相关代码
def makeCounter() {
def very_local_variable = 0;
return {very_local_variable += 1}
}
c1 = makeCounter()
c1()
c1()
c1()
c2 = makeCounter()
println "C1 = ${c1()}, C2 = ${c2()}"
他继续说C1 = 4,将打印C2 = 1。这是怎么发生的?我确信我在这里缺乏理解可能源于Groovy如何工作的概念性失败,或者可能在Groovy,Scala等函数式语言中存在一些通用性。方法中的局部变量是否保持其状态,直到再次调用该函数为止分配给另一个变量? (谷歌搜索“功能计数器groovy | scala”没有带来任何结果)
答案 0 :(得分:7)
我不知道为什么这个问题没有答案,但它可能至少应该为未来的访客提供一个答案。
您上面写的是closure的一个简单示例。闭包是一种与语言无关的想法,根本不依赖于Groovy。你总是使用它们,例如在JavaScript中。
闭包本质上是一个函数或对函数的引用以及引用环境。
在您的示例中,每次调用makeCounter
都会引入一个新的本地very_local_variable
,我认为很清楚。你在该函数的第二行中所做的是返回一个不带参数的闭包({ .. }
语法)并返回以1递增的局部变量。闭包可以访问该变量,因为它存在(它是引用环境的一部分)。
每次调用makeCounter
都会创建一个新的局部变量并返回一个新的闭包,因此每个闭包都在自己的局部变量/环境中运行。
实际上,如果你来自爪哇岛,那对你来说应该不是那么新鲜。您有匿名类,可以访问最终变量,例如
Runnable makeCounter() {
final int[] very_local_variable = {0};
return new Runnable() {
@Override
public void run() {
very_local_variable[0] += 1;
}
};
}
Runnable c1 = makeCounter();
c1.run();
c1.run();
c1.run();
将局部变量声明为数组,将其作为Runnable
仿函数返回等等......这一切都来自Java的设计和限制,但实质上它与您的代码非常接近。 Java 8更加接近差距:
Runnable makeCounter() {
int[] very_local_var = {0};
return () -> { very_local_variable[0] += 1; };
}