“闭包”和“块”到底有什么区别?
-
06-07-2019 - |
题
我发现很多人都用这个词 关闭 和 堵塞 可以互换。这些人中的大多数无法解释他们在说什么。
一些 Java 程序员(甚至是来自非常昂贵的咨询公司的程序员)将匿名内部类称为“块”和“闭包”——但我知道这不是真的。(您不能从定义可变变量的方法的范围内传递可变变量......)
我在找:
- 精确的计算机科学 块的定义
- 精确的计算机科学 闭包的定义
- 和 对差异的澄清 两者之间。
我真的很想查看链接、文章或书籍 请参考这些.
解决方案
虽然一个 堵塞 只是一段可以由语句和声明组成的代码,仅此而已, 关闭 是一个真正的一流对象,一个以块作为其值的真正变量。
主要区别在于块只是将指令组合在一起(例如 尽管 语句),而闭包是一个包含一些可以执行的代码的变量。
如果你有一个闭包,通常你可以将它作为参数传递给函数,对其进行 currify 和 decurrify,然后主要调用它!
Closure c = { println 'Hello!' }
/* now you have an object that contains code */
c.call()
当然,闭包更强大,它们是变量,可以用来定义对象的自定义行为(而通常你必须使用 接口 或编程中的其他 OOP 方法)。
你可以想一个 关闭 作为一个包含该函数在其内部执行的操作的函数。
块很有用,因为它们允许确定变量的范围。通常,当您在作用域内定义变量时,您可以覆盖外部定义而不会出现任何问题,并且新定义将在块执行期间存在。
for (int i = 0; i < 10; ++i)
{
int t = i*2;
printf("%d\r\n", t);
}
t
是在块内部定义的(块的主体) for
语句)并将持续在该块内。
其他提示
块是语法上的东西 - 语句的逻辑单元(与 范围 比 关闭).
if (Condition) {
// Block here
}
else {
// Another block
}
闭包与匿名函数或类相关 - 匿名(函数)对象,绑定到环境(及其变量)的一段代码。
def foo() {
var x = 0
return () => { x += 1; return x }
}
这里 foo
返回一个闭包!局部变量 x
即使在关闭之后仍然存在 foo
已终止,并且可以通过调用返回的匿名函数来递增。
val counter = foo()
print counter() // Returns 2
print counter() // Return 3
请注意,这只是 Ruby,其中块和闭包的处理方式类似,因为 Ruby 称之为块 是一个 关闭:
(1..10).each do |x|
p x
end
那里 each
-方法传递一个闭包函数(带有参数x),称为 堵塞 在红宝石中。
这里存在很多混乱,因为有些术语具有多个定义,并且多个不同的事物仅仅因为它们通常一起被发现而被混淆。
首先,我们有“块”。这只是构成一个单元(例如循环体)的词汇代码块。如果该语言实际上具有块作用域,则可以定义仅存在于该代码块中的变量。
其次,我们有可调用代码作为值类型。在函数式语言中,这些是函数值——有时称为“funs”、“匿名函数”(因为函数是在值中找到的,而不是在其分配的名称中找到的;你不需要一个名字来称呼它们),或者“lambdas”(来自用于在 Church 的 Lambda 演算中创建它们的运算符)。它们可能被称为“闭包”,但它们并不自动成为真正的闭包;为了符合资格,它们必须封装(“封闭”)围绕其创建的词法范围 - 也就是说,在函数本身范围之外但在其定义范围内定义的变量在调用函数时仍然可用,甚至如果调用点位于引用的变量之后,否则就会超出范围并回收其存储。
例如,考虑这个 JavaScript:
function makeClosure() {
var x = "Remember me!";
return function() {
return "x='" + x + "'";
}
}
// console.log(x);
// The above is an error; x is undefined
var f = makeClosure();
console.log(f());
// The above outputs a string that includes x as it existed when f was created.
变量 x
仅在函数体内定义 makeClosure
;在该定义之外,它不存在。我们打电话后 makeClosure
, , 这 x
里面声明的应该消失了。从大部分代码的角度来看确实如此。但返回的函数 makeClosure
被宣布时 x
存在,因此当您稍后调用它时它仍然可以访问它。这使它成为真正的关闭。
您可以使用非闭包的函数值,因为它们不保留范围。您也可以部分关闭;PHP 的函数值仅保留在创建值时必须列出的特定变量。
您还可以拥有根本不代表整个函数的可调用代码值。Smalltalk 称这些为“块闭包”,而 Ruby 称它们为“procs”,尽管许多 Rubyists 只称它们为“块”,因为它们是由 Ruby 创建的内容的具体化版本。 {
...}
或者 do
...end
句法。它们与 lambda(或“函数闭包”)的区别在于它们不引入新的调用级别。 如果块闭包体内的代码调用 return
, ,它从存在于其中的块闭包的外部函数/方法返回,而不仅仅是块本身。
这种行为对于保留 R.D. 的内容至关重要。Tennent 标记为“对应原则”,它指出您应该能够用包含该代码的内联函数替换任何代码,并立即调用该函数。例如,在 Javascript 中,您可以将其替换为:
x=2
console.log(x)
有了这个:
(function(){x = 2;})();
console.log(x)
这个例子不是很有趣,但是在不影响程序行为的情况下进行这种转换的能力在功能重构中起着关键作用。但是对于 lambda,一旦你嵌入了 return
陈述,原则不再成立:
function foo1() {
if (1) {
return;
}
console.log("foo1: This should never run.")
}
foo1()
function foo2() {
if (1) {
(function() { return; })();
}
console.log("foo2: This should never run.")
}
foo2()
第二个功能与第一个功能不同;这 console.log
被执行,因为 return
只从匿名函数返回,而不是从 foo2
. 。这就破坏了对应原则。
这就是 Ruby 同时具有 proc 和 lambda 的原因,尽管这种区别一直是新手感到困惑的根源。procs 和 lambda 都是类的对象 Proc
, ,但它们的行为不同,如上所述:A return
只从 lambda 主体返回,但它从 proc 周围的方法返回。
def test
p = proc do return 1 end
l = lambda do return 1 end
r = l[]
puts "Called l, got #{r}, still here."
r = p[]
puts "Called p, got #{r}, still here?"
end
以上 test
方法永远不会到达第二个 puts
, ,因为调用 p
会引发 test
立即返回(返回值为 1)。如果 Javascript 有块闭包,你可以做同样的事情,但它没有(尽管有人建议添加它们)。
您使用的术语是最常用的 一起 尽管这些构造以前出现在 Algol、Smalltalk 和 Scheme 中,但如今已在 Ruby 中出现。如果有的话,我会引用 Ruby 标准。
我不确定我能否回答你的确切问题,但我可以举例说明。如果您已经知道这一点,我很抱歉...
def f &x
yield
x
end
def g
y = "block"
t = f { p "I'm a #{y}" }
y = "closure"
t
end
t = g
t.call
和...
$ ruby exam.rb
"I'm a block"
"I'm a closure"
$
所以一个块是 附加到方法调用的类似匿名函数的代码序列。它在整个 Ruby API 中都有使用。当您使创建匿名函数变得足够容易时,事实证明它们对各种事情都很有用。
但请注意,之后 f
返回,则 g
返回,我们通过将其返回来保留该块 f
(作为 x
)然后从 g
(作为 t
)。现在我们第二次调用该块。再次请注意 g()
已经回来了。但是该块引用了不再存在的函数实例(和作用域)中的局部变量?!它得到的新值是 y
?!
所以闭包是 一个类似函数的对象,其词法范围是封闭的。它们的实现非常具有挑战性,因为它们破坏了对函数调用实例中的局部变量非常有用的堆栈模型。
1.Ruby 有多种类似闭包的函数对象;这只是其中之一。
5
那是一个 整数.
Int workDaysInAWeek = 5
那是一个 整型变量 并且它可能被设置为不同的 整数. 。(如果情况阻止您修改该值,则它可能被称为 持续的.)
鉴于上述令人担忧的数字, 块 和 关闭 关注算法。之间的区别 块 和 关闭, ,分别也等价于上面。