Pregunta

He descubierto que muchas personas usan las palabras cierre y bloquear indistintamente. La mayoría de estas personas no pueden explicar de qué están hablando.

Algunos programadores de Java (incluso los de consultorías realmente caras) hablan de clases internas anónimas como "bloques". y "cierres" - Pero sé que esto no es cierto. (No puede pasar variables mutables desde el alcance del método en el que están definidas ...)

Estoy buscando:

  • una definición precisa, informática de un bloque
  • una definición precisa, informática de un cierre
  • y aclaraciones sobre la diferencia entre los dos.

Realmente me gustaría ver enlaces, artículos o referencias de libros sobre estos, por favor .

¿Fue útil?

Solución

Mientras que un bloque es solo un fragmento de código que puede estar compuesto por declaraciones y declaraciones, pero nada más, un cierre es un objeto real de primera clase, un verdadero variable que tiene un bloque como su valor.

La principal diferencia es que un bloque simplemente agrupa las instrucciones (por ejemplo, el cuerpo de una instrucción while ), mientras que un cierre es una variable que contiene algún código que se puede ejecutar.

Si tiene un cierre, generalmente puede pasarlo como un parámetro a las funciones, currificarlo y decurrificarlo, ¡y principalmente llamarlo!

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

Por supuesto, los cierres son más potentes, son variables y se pueden usar para definir el comportamiento personalizado de los objetos (mientras que generalmente tenía que usar interfaces u otros enfoques OOP en la programación).

Puede pensar en un cierre como una función que contiene lo que esa función hace dentro de sí misma.

Los bloques son útiles porque permiten el alcance de las variables. Por lo general, cuando define una variable dentro de un ámbito, puede anular las definiciones externas sin ningún problema y existirán nuevas definiciones solo durante la ejecución del bloque.

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

t se define dentro del bloque (el cuerpo de la instrucción for ) y durará justo dentro de ese bloque.

Otros consejos

Un bloque es algo sintáctico: una unidad lógica de declaraciones (más relacionadas con alcance que con cierre ).

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

Un cierre está relacionado con funciones o clases anónimas: un objeto anónimo (función), un fragmento de código que está vinculado a un entorno (con sus variables).

def foo() {
   var x = 0
   return () => { x += 1; return x }
}

¡Aquí foo devuelve un cierre! La variable local x persiste durante el cierre incluso después de que foo finalice y puede incrementarse mediante llamadas de la función anónima devuelta.

val counter = foo()
print counter() // Returns 2
print counter() // Return 3

Tenga en cuenta que es solo Ruby en el que el bloque y el cierre se tratan de manera similar, ya que lo que Ruby llama bloque es un cierre :

(1..10).each do |x|
    p x
end

Allí cada -método se pasa una función de cierre (tomando un parámetro x) que se llama un bloque en Ruby.

Hay mucha confusión por aquí porque hay términos con múltiples definiciones y varias cosas distintas que se combinan simplemente porque generalmente se encuentran juntas.

Primero, tenemos "bloque". Eso es solo un fragmento de código léxico que hace que una unidad, el cuerpo de un bucle, por ejemplo. Si el lenguaje tiene realmente un alcance de bloque, entonces se pueden definir variables que solo existen dentro de ese fragmento de código.

Segundo, tenemos un código invocable como un tipo de valor. En lenguajes funcionales, estos son valores de función, a veces llamados '' funs '', '' funciones anónimas '' (porque la función se encuentra en el valor, no en el nombre al que está asignada; no necesita un nombre para llamarlos) o "lambdas" (del operador utilizado para crearlos en el cálculo Lambda de Church). Pueden llamarse cierres, pero no son cierres verdaderos automáticamente; Para calificar, deben encapsular (`` cerrar sobre '') el alcance léxico que rodea su creación, es decir, las variables definidas fuera del alcance de la función en sí pero dentro del alcance de su definición todavía están disponibles cada vez que se llama a la función, incluso si el punto de llamada es posterior a la variable referenciada, de lo contrario se habría salido del alcance y se habría reciclado su almacenamiento.

Por ejemplo, considere este 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.

La variable x se define solo dentro del cuerpo de la función makeClosure ; fuera de esa definición, no existe. Después de llamar a makeClosure , el x declarado en su interior debería desaparecer. Y lo es, desde el punto de vista de la mayoría del código. Pero la función devuelta por makeClosure se declaró mientras existía x , por lo que aún tiene acceso a ella cuando la llame más tarde. Eso lo convierte en un verdadero cierre.

Puede tener valores de función que no sean cierres, porque no conservan el alcance. También puede tener cierres parciales; Los valores de función de PHP solo conservan variables específicas que deben enumerarse en el momento en que se crea el valor.

También puede tener valores de código invocables que no representan funciones completas en absoluto. Smalltalk llama a estos '' bloqueos de cierre '', mientras que Ruby los llama '' procs '', aunque muchos Rubyistas simplemente los llaman '' bloques '', porque son la versión reificada de lo que es creado por el {. .. } o do ... Sintaxis end . Lo que los distingue de lambdas (o `` cierres de funciones '') es que no introducen un nuevo nivel de llamada. Si el código en el cuerpo de un cierre de bloque invoca return , vuelve de la función / método externo dentro del cual se encuentra el cierre de bloque, no solo el bloque en sí.

Ese comportamiento es crítico para preservar lo que R.D. Tennent etiquetó como el "principio de correspondencia", que establece que debería ser capaz de reemplazar cualquier código con una función en línea que contenga ese código en el cuerpo y llame de inmediato. Por ejemplo, en Javascript, puede reemplazar esto:

x=2
console.log(x)

con esto:

(function(){x = 2;})();
console.log(x)

Ese ejemplo no es muy interesante, pero la capacidad de hacer este tipo de transformación sin afectar el comportamiento del programa juega un papel clave en la refactorización funcional. Pero con lambdas, tan pronto como haya incrustado declaraciones return , el principio ya no se cumple:

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()

La segunda función es diferente de la primera; console.log se ejecuta porque return solo regresa de la función anónima, no de foo2 . Esto rompe el principio de correspondencia.

Es por eso que Ruby tiene procs y lambdas, a pesar de que la distinción es una fuente perenne de confusión para los novatos. Tanto los procs como los lambdas son objetos de la clase Proc , pero se comportan de manera diferente, como se indicó anteriormente: un return simplemente regresa del cuerpo de un lambda, pero regresa del método rodeando un proceso.

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

El método anterior test nunca llegará al segundo put , porque la invocación de p causará test para volver inmediatamente (con un valor de retorno de 1). Si Javascript tuviera bloqueos de cierre, podría hacer lo mismo, pero no es así (aunque hay una propuesta para agregarlos).

El ruidoso y barbudo tiene esto que decir sobre Cierres y Bloques:

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

En un momento, dice que un cierre es un bloque que se puede pasar como argumento a un método.

Los términos que está utilizando se usan más comúnmente juntos en estos días en Ruby, aunque las construcciones aparecieron previamente en Algol, Smalltalk y Scheme. Citaría el estándar Ruby, si hubiera uno.

No estoy seguro de poder responder tu pregunta exacta, pero puedo ilustrarlo. Mis disculpas si ya lo sabes ...

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

Y ...

$ ruby exam.rb
"I'm a block"
"I'm a closure"
$ 

Entonces, un bloque es una secuencia anónima de código similar a una función adjunta a una llamada al método. Se usa en toda la API de Ruby. Cuando facilita la creación de una función anónima resulta que son útiles para todo tipo de cosas.

Pero tenga en cuenta que después de que f regresa, entonces g regresa, nos aferramos al bloque devolviéndolo desde f (como x ) y luego desde g (como t ). Ahora llamamos al bloque por segunda vez. Nuevamente, tenga en cuenta que g () ha regresado. ¿Pero el bloque se refiere a una variable local en una instancia de función (y alcance) que ya no existe? ¡Y obtiene el nuevo valor de y ?!

Entonces, un cierre es un objeto similar a una función que se cierra sobre su alcance léxico. Resultan bastante difíciles de implementar porque destruyen el modelo de hágalo con una pila que es muy útil para las variables locales en instancias de llamadas a funciones.


1. Ruby tiene varios sabores de objetos con función de cierre; Este es solo uno de ellos.

  

5

Eso es un entero .

  

Int workDaysInAWeek = 5

Esa es una variable entera y se puede establecer en un entero diferente. (Si las circunstancias le impiden modificar ese valor, puede llamarse una constante .)

Mientras que los números de preocupación anteriores, bloques y cierres se refieren a algoritmos. La distinción entre bloques y cierres , respectivamente, también es equivalente a lo anterior.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top