“闭合”和“块”之间究竟有什么区别?

时间:2009-11-28 12:18:38

标签: programming-languages computer-science theory language-theory

我发现很多人可以互换地使用 closure block 这两个词。这些人中的大多数都无法解释他们在说什么。

一些Java程序员(甚至来自真正昂贵的咨询公司)将匿名内部类称为“块”和“闭包” - 但我知道这不是真的。 (你不能从定义它们的方法范围中传递可变变量......)

我正在寻找:

  • 精确的计算机科学块的定义
  • 精确的计算机科学闭包定义
  • 澄清两者之间的区别

我非常希望看到关于这些的链接,文章或书籍参考

6 个答案:

答案 0 :(得分:30)

虽然只是一段代码,可以由语句和声明组成,但没有别的,闭包是一个真正的一流对象,一个真实的将块作为其值的变量。

主要区别在于块只是将指令组合在一起(例如 while 语句的主体),而闭包是包含一些可以执行的代码的变量。

如果你有一个闭包,你可以将它作为参数传递给函数,对它进行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语句的主体),并且将在该块内部持续。

答案 1 :(得分:18)

一个块是语法的东西 - 一个逻辑的语句单元(更多与范围相关而非闭包)。

if (Condition) {
    // Block here 
} 
else {
    // Another block
}

闭包与anoymous函数或类相关 - 匿名(函数)对象,绑定到环境(带有变量)的一段代码。

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),在Ruby中称为

答案 2 :(得分:6)

这里存在很多混淆,因为有多个定义的术语,以及多个不同的东西,因为它们通常一起被发现而混淆。

首先,我们有“阻止”。这只是一个词汇的代码块,它构成了一个单元 - 例如循环的主体。如果语言实际上具有块范围,则可以定义仅存在于该代码块中的变量。

其次,我们将可调用代码作为值类型。在函数式语言中,这些是函数值 - 有时称为“funs”,“匿名函数”(因为函数位于值中,而不是其赋值的名称;您不需要名称来调用它们),或者“ lambdas“(来自运营商用于在Church's Lambda Calculus中创建它们)。它们可能被称为“闭包”,但它们不是自动真正的闭包;为了符合条件,他们必须封装(“关闭”)围绕其创建的词法范围 - 也就是说,在函数本身范围之外定义但在其定义范围内的变量在调用函数时仍然可用,甚至如果调用点位于引用变量之后,否则它将超出范围并使其存储器被回收。

例如,请考虑以下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”,尽管许多Rubyist只是称它们为“块”,因为它们是{所创建内容的具体版本...... {{1} }或} ... do语法。使它们与lambdas(或“函数闭包”)不同的是它们不引入新的调用级别。 如果块闭包体中的代码调用end,则它从外部函数/方法返回块闭包存在于其中,而不仅仅是块本身。

这种行为对于保留R.D. Tennent标记的“对应原则”至关重要,该原则指出您应该能够使用包含该代码的内联函数替换任何代码并立即调用。例如,在Javascript中,您可以替换它:

return

用这个:

x=2
console.log(x)

这个例子不是很有趣,但是在不影响程序行为的情况下进行这种转换的能力在功能重构中起着关键作用。但是对于lambdas,只要你嵌入(function(){x = 2;})(); console.log(x)语句,原则就不再成立:

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返回。这打破了通信原则。

这就是为什么Ruby既有procs又有lambdas,尽管这种区别是新手常常混淆的原因。 procs和lambdas都是类foo2的对象,但它们的行为不同,如上所示:Proc只是从lambda的主体返回,但它从proc周围的方法返回。

return

以上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立即返回(返回值为1) 。如果Javascript有块闭包,你可以做同样的事情,但事实并非如此(虽然有提议添加它们)。

答案 3 :(得分:2)

吵闹的大胡子有关于封闭和阻挡的说法:

http://martinfowler.com/bliki/Closure.html

有一次他说闭包是一个可以作为参数传递给方法的块。

答案 4 :(得分:1)

目前,您使用的术语在Ruby中最常用于 ,尽管之前的构造出现在Algol,Smalltalk和Scheme中。我会引用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)然后从{{返回}来保留该块。 1}}(作为g)。现在我们第二次调用该块。再次注意t已经返回。但是块引用了一个不再存在的函数实例(和范围)中的局部变量?!它获得g()的新值?!

所以一个闭包是一个类似于函数的对象,它在其词法范围内被关闭。实现它们非常具有挑战性,因为它们会破坏堆栈操作模型,这对于函数调用实例中的局部变量非常有用。


1。 Ruby有各种类似闭包的函数对象;这只是其中之一。 功能

答案 5 :(得分:0)

  

5

那是整数

  

Int workDaysInAWeek = 5

这是整数变量,它可以设置为不同的整数。 (如果情况阻止您修改该值,则可以将其称为常量。)

虽然上述关注数字,闭包涉及算法。 闭包之间的区别也分别等同于上述。