В чем разница между «закрытием» и «блоком»? - PullRequest
45 голосов
/ 28 ноября 2009

Я обнаружил, что многие люди используют слова замыкание и блок взаимозаменяемо. Большинство из этих людей не могут объяснить, о чем они говорят.

Некоторые Java-программисты (даже из действительно дорогих консультантов) говорят об анонимных внутренних классах как «блоки» и «замыкания» - но я знаю, что это не так. (Вы не можете передавать изменяемые переменные из области действия метода, в котором они определены ...)

Я ищу:

  • точная информатика определение блока
  • точная информатика определение замыкания
  • и уточнение разницы между двумя.

Мне бы очень хотелось, чтобы ссылки, статьи или книги ссылки на них, пожалуйста, .

Ответы [ 6 ]

30 голосов
/ 28 ноября 2009

В то время как блок - это просто кусок кода, который может быть составлен с помощью операторов и объявлений, но не более того, замыкание - это настоящий объект первого класса, настоящая переменная, которая имеет блок в качестве значения.

Основное отличие состоит в том, что блок просто группирует инструкции (например, тело оператора while ), тогда как замыкание является переменной, содержащей некоторый код, который может быть выполнен.

Если у вас есть замыкание, обычно вы можете передавать его в качестве параметра функциям, проверять и очищать его и, в основном, вызывать его!

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

Конечно, замыкания являются более мощными, они являются переменными и могут использоваться для определения пользовательского поведения объектов (в то время как обычно вам приходилось использовать интерфейсы или другие подходы ООП в программировании).

Вы можете думать о замыкании как о функции, которая содержит то, что эта функция делает внутри себя.

Блоки полезны, потому что они позволяют определять переменные. Обычно, когда вы определяете переменную внутри области видимости, вы можете без проблем переопределить внешние определения, и новые определения будут существовать только во время выполнения блока.

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

t определяется внутри блока (тело оператора for) и будет действовать только внутри этого блока.

18 голосов
/ 28 ноября 2009

Блок - это что-то синтаксическое - логическая единица операторов (больше относится к scope , чем к closure ).

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), которая в Ruby называется block .

6 голосов
/ 09 мая 2014

Здесь много путаницы, потому что есть термины с несколькими определениями и множеством разных вещей, которые смешиваются просто потому, что они обычно встречаются вместе.

Во-первых, у нас есть «блок». Это просто лексическая часть кода, которая составляет единицу - например, тело цикла. Если язык на самом деле имеет область видимости блока, то можно определить переменные, которые существуют только в этом фрагменте кода.

Во-вторых, у нас есть вызываемый код как тип значения. В функциональных языках это значения функций - иногда называемые «funs», «anonymous functions» (потому что функция находится в значении, а не в имени, которому она назначена; вам не нужно имя для их вызова) или « лямбды »(от оператора, использовавшегося для их создания в церковном лямбда-исчислении). Их можно назвать «замыканиями», но они не являются автоматически истинными замыканиями; для квалификации они должны инкапсулировать («закрыть») лексическую область, окружающую их создание, то есть переменные, определенные вне области самой функции, но в рамках ее определения, по-прежнему доступны при каждом вызове функции, даже если вызывающая точка находится после указанной переменной, в противном случае она вышла бы из области видимости и использовала бы хранилище повторно.

Например, рассмотрим этот 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 называет их «процессами», хотя многие Rubyists просто называют их «блоками», потому что они являются версией того, что создано { ... } или * 1026. * ... end синтаксис. Что отличает их от лямбд (или «замыканий функций»), так это то, что они не вводят новый уровень вызовов. Если код в теле закрытия блока вызывает return, он возвращает из внешней функции / метода, в котором существует замыкание блока, а не только сам блок.

Такое поведение имеет решающее значение для сохранения того, что Р.Д. Теннент назвал «принципом соответствия», в котором говорится, что вы должны иметь возможность заменить любой код встроенной функцией, содержащей этот код в теле, и вызываться немедленно. Например, в Javascript вы можете заменить это:

x=2
console.log(x)

с этим:

(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 возвращается только из анонимной функции, а не из foo2. Это нарушает принцип соответствия.

Именно поэтому в Ruby есть как пробы, так и лямбды, хотя это различие является постоянным источником путаницы для новичков. И procs, и lambdas являются объектами класса Proc, но они ведут себя по-разному, как указано выше: a return просто возвращается из тела лямбды, но возвращается из метода, окружающего 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 есть закрытие блоков, вы можете сделать то же самое, но это не так (хотя есть предложение добавить их).

2 голосов
/ 29 ноября 2009

Громкий, бородатый говорит о замыканиях и блоках:

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

В какой-то момент он говорит, что замыкание - это блок, который можно передать в качестве аргумента методу.

1 голос
/ 29 ноября 2009

Термины, которые вы используете, чаще всего используются вместе в эти дни в 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), а затем из g (как t). Теперь мы вызываем блок во второй раз. Снова обратите внимание, что g() вернулся. Но блок ссылается на локальную переменную в экземпляре функции (и области видимости), которая больше не существует ?! И он получает новое значение y ?!

Таким образом, замыкание является функциональным объектом, который закрыт по своей лексической области видимости. Их довольно сложно реализовать, потому что они разрушают модель «делай со стеком», которая так полезна для локальных переменных в экземплярах вызовов функций.


1. В Ruby есть различные разновидности объектов, подобных замыканиям; это только один из них.

0 голосов
/ 14 января 2016

5

Это целое число .

Int workDaysInAWeek = 5

Это целая переменная , и она может быть установлена ​​на другое целое число . (Если обстоятельства не позволяют изменить это значение, его можно назвать константой .)

Принимая во внимание, что приведенные выше числа относятся, блоки и замыкания относятся к алгоритмам. Различие между блоками и пробками , соответственно, также эквивалентно приведенному выше.

...