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

Daf*_*ees 45 theory computer-science programming-languages language-theory

我发现很多人使用闭包互换的词.这些人中的大多数都无法解释他们在谈论什么.

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

我在找:

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

我真的很想看到关于这些的链接,文章或书籍参考.

Jac*_*ack 30

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

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

如果你有一个闭包通常你可以把它作为参数传递给函数,currify和decurrify它,主要是调用它!

Closure c = { println 'Hello!' }
/* now you have an object that contains code */
c.call()
Run Code Online (Sandbox Code Playgroud)

当然闭包更强大,它们是变量,可以用来定义对象的自定义行为(虽然通常你必须在编程中使用接口或其他OOP方法).

您可以将闭包视为包含该函数本身内容的函数.

块很有用,因为它们允许变量的范围.通常,当您在范围内定义变量时,您可以覆盖外部定义而不会出现任何问题,并且在执行块期间将存在新的定义.

for (int i = 0; i < 10; ++i)
{
     int t = i*2;
     printf("%d\r\n", t);
}
Run Code Online (Sandbox Code Playgroud)

t在块(for语句的主体)内定义,并且将在该块内部持续.


Dar*_*rio 18

块是句法的东西 - 语句的逻辑单元(更多地与范围相关而不是与闭包相关).

if (Condition) {
    // Block here 
} 
else {
    // Another block
}
Run Code Online (Sandbox Code Playgroud)

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

def foo() {
   var x = 0
   return () => { x += 1; return x }
}
Run Code Online (Sandbox Code Playgroud)

这里foo返回一个闭包!x即使在foo终止之后,局部变量仍然通过闭包持续存在,并且可以通过调用返回的匿名函数来递增.

val counter = foo()
print counter() // Returns 2
print counter() // Return 3
Run Code Online (Sandbox Code Playgroud)

请注意,只是Ruby中的块和闭包被类似地处理,因为Ruby调用块是一个闭包:

(1..10).each do |x|
    p x
end
Run Code Online (Sandbox Code Playgroud)

each-method传递其被称为一个闭合功能(拍摄参数x)中的Ruby.


Mar*_*eed 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.
Run Code Online (Sandbox Code Playgroud)

变量x仅在函数体内定义makeClosure; 在该定义之外,它不存在.我们打电话后makeClosure,x声明里面应该消失了.从大多数代码的角度来看,它都是.但是返回的函数makeClosurex存在时被声明,所以当你稍后调用它时它仍然可以访问它.这使它成为一个真正的关闭.

您可以使用不是闭包的函数值,因为它们不保留作用域.你也可以部分关闭; PHP的函数值仅保留在创建值时必须列出的特定变量.

您还可以拥有根本不代表整个函数的可调用代码值.Smalltalk称这些为"块闭包",而Ruby称它们为"procs",尽管许多Rubyist只是将它们称为"块",因为它们是由{... }do... end语法创建的内容的具体版本.使它们与lambdas(或"函数闭包")不同的是它们不引入新的调用级别. 如果块闭包体中的代码调用return,则从外部函数/方法返回块闭包存在于其中,而不仅仅是块本身.

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

有了这个:

(function(){x = 2;})();
console.log(x)
Run Code Online (Sandbox Code Playgroud)

这个例子不是很有趣,但是在不影响程序行为的情况下进行这种转换的能力在功能重构中起着关键作用.但是对于lambdas,只要你有嵌入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()
Run Code Online (Sandbox Code Playgroud)

第二个功能与第一个功能不同; 在console.log被执行,因为return从匿名函数只返回,而不是从foo2.这打破了通信原则.

这就是为什么Ruby既有procs又有lambdas,尽管这种区别是新手常年混淆的原因.procs和lambdas都是类的对象Proc,但它们的行为不同,如上所示:一个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
Run Code Online (Sandbox Code Playgroud)

上面的test方法永远不会到达第二个puts,因为调用p会导致test立即返回(返回值为1).如果Javascript有块闭包,你可以做同样的事情,但事实并非如此(尽管有提议添加它们).