Каково точное определение замыкания?

Я читал предыдущие темы о замыканиях стекового потока и других источниках, и одна вещь все еще смущает меня. Из того, что я смог соединить технически, замыкание представляет собой просто набор данных, содержащий код функции и значениеbound variables в этой функции.

Другими словами, технически следующая функция C должна быть закрыта от моего понимания:

int count()
{
    static int x = 0;

    return x++;
}

Однако все, что я читаю, похоже, подразумевает, что замыкания должны как-то включать передачу функций в качестве объектов первого класса. Кроме того, обычно подразумевается, что замыкания не являются частью процедурного программирования. Это тот случай, когда решение чрезмерно связано с проблемой, которую оно решает, или я неправильно понимаю точное определение?

 Mehrdad Afshari08 июл. 2009 г., 03:32
Википедия особенно хороша в определениях:en.wikipedia.org/wiki/Closure_%28computer_science%29
 Amaron08 июл. 2009 г., 04:39
Да, но я думал, что определение вики неверно. Теперь я вижу, что вся моя проблема решена вокруг факта, что & quot; свободные переменные & quot; в информатике не такие, как в математике.
 Robert Harvey♦08 июл. 2009 г., 03:46
Однако подобные определения в Википедии, как правило, пишутся уродцами-трубочниками и кристаллографами (то есть они не всегда являются простейшими для понимания птицами).
 Amaron08 июл. 2009 г., 03:48
Статья в Википедии была той, которую я прочитал. Это говорит о том, что любая функция первого класса со свободными переменными является замыканием. Это не имеет никакого смысла, потому что включает в себя все функции первого класса, которые возвращают значения на основе своих аргументов. Означает ли это, что оно становится закрытием, когда функция использует свободные переменные для создания экземпляров связанных переменных?
 Nosredna08 июл. 2009 г., 04:04
Ну, очевидно, функция С не является первым классом.

Ответы на вопрос(8)

это не закрытие. Ваш пример - это просто функция, которая возвращает результат приращения статической переменной.

Вот как будет работать замыкание:

function makeCounter( int x )
{
  return int counter() {
    return x++;
  }
}

c = makeCounter( 3 );
printf( "%d" c() ); => 4
printf( "%d" c() ); => 5
d = makeCounter( 0 );
printf( "%d" d() ); => 1
printf( "%d" c() ); => 6

Другими словами, различные вызовы makeCounter () создают разные функции с их собственной привязкой переменных в их лексической среде, которую они "закрыли".

Изменить: я думаю, что подобные примеры делают замыкания легче для понимания, чем определения, но если вам нужно определение, которое я бы сказал, «Закрытие - это комбинация функции и среды. Среда содержит переменные, которые определены в функции, а также переменные, которые видны функции при ее создании. Эти переменные должны оставаться доступными для функции, пока она существует. & Quot;

Решение Вопроса

что я понимаю, замыкание также должно иметь доступ к переменным в контексте вызова. Замыкания обычно связаны с функциональным программированием. Языки могут иметь элементы из разных типов программных точек зрения: функциональных, процедурных, императивных, декларативных и т. Д. Они получают свое имя от закрытия в определенном контексте. Они также могут иметь лексическую привязку в том смысле, что они могут ссылаться на указанный контекст с теми же именами, которые используются в этом контексте. Ваш пример не имеет ссылки на какой-либо другой контекст, кроме глобального статического.

ОтВикипедия

Замыкание закрывает свободные переменные (переменные, которые не являются локальными переменными)

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

Предположим, что этот фрагмент кода C # (который должен выполнятьAND поиск в списке):

List<string> list = new List<string> { "hello world", "goodbye world" };
IEnumerable<string> filteredList = list;
var keywords = new [] { "hello", "world" };
foreach (var keyword in keywords)
    filteredList = filteredList.Where(item => item.Contains(keyword));

foreach (var s in filteredList)  // closure is called here
    Console.WriteLine(s);

Это обычная ловушка в C #, чтобы делать что-то подобное. Если вы посмотрите на лямбда-выражение внутриWhereВы увидите, что он определяет функцию, поведение которой зависит от значения переменной на ее сайте определения. Это похоже на прохождениеvariable itself к функции, а неvalue of that variable, Фактически, когда вызывается это замыкание, оно получает значениеkeyword переменная в то время. Результат этого образца очень интересный. Распечатываетboth & quot; Привет, мир & quot; и "до свидания мир", что не то, что мы хотели. Что случилось? Как я уже говорил выше, функция, которую мы объявили с помощью лямбда-выражения:a closure over keyword variable так вот что происходит:

filteredList = filteredList.Where(item => item.Contains(keyword))
                           .Where(item => item.Contains(keyword)); 

и во время выполнения закрытия,keyword имеет значение & quot; мир & quot; поэтому мы в основном фильтруем список пару раз с одним и тем же ключевым словом. Решение:

foreach (var keyword in keywords) {
    var temporaryVariable = keyword;
    filteredList = filteredList.Where(item => item.Contains(temporaryVariable));
}

посколькуtemporaryVariable относится кbody изforeach цикл, в каждой итерации, это разные переменные. По сути, каждое замыкание будет привязано к отдельной переменной (это разные случаиtemporaryVariable на каждой итерации). На этот раз он даст правильные результаты («Привет, мир»):

filteredList = filteredList.Where(item => item.Contains(temporaryVariable_1))
                           .Where(item => item.Contains(temporaryVariable_2));

в которомtemporaryVariable_1 имеет значение "привет" а такжеtemporaryVariable_2 имеет значение "мир" во время выполнения закрытия.

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

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

Все выражения, включая функции, оцениваются вenvironementОкружающая среда представляет собой последовательностьframes, Кадр отображает имена переменных в значения. Каждый кадр также имеет указатель на окружающую среду. Функция оценивается в новой среде с фреймом, содержащим привязки для ее аргументов. Теперь давайте посмотрим на следующий интересный сценарий. Представьте, что у нас есть функцияaccumulator, который при оценке вернет другую функцию:

// This is some C like language that has first class functions and closures.
function accumulator(counter) {
    return (function() { return ++counter; });
}

Что будет, когда мы оценим следующую строку?

accum1 = accumulator(0);

Сначала создается новая среда и целочисленный объект (дляcounter) связан с 0 в первом кадре. Возвращаемое значение, которое является новой функцией, привязано к глобальной среде. Обычно новая среда будет собирать мусор после того, как функция оценка окончена. Здесь этого не произойдет.accum1 содержит ссылку на него, так как ему нужен доступ к переменнойcounter, когдаaccum1 называется, это будет увеличивать значениеcounter в среде, на которую ссылаются. Теперь мы можем позвонитьaccum1 функция с локальным состоянием или замыканием.

Я описал несколько практических применений замыканий в моем блоге http://vijaymathew.wordpress.com, (См. Сообщения «Опасные конструкции» и «О передаче сообщений»).

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

(Многое из этого взято из "Groovy in Action" Дирка Кенига, замечательной книги)

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

По словам Дирка, представьте конверт с листком бумаги внутри. Типичный объект будет иметь переменные и их значения, написанные на этом документе, но вместо замыкания будет список инструкций. Допустим, в письме говорится: «Дайте этот конверт и письмо своим друзьям».

In Groovy: Closure envelope = { person -> new Letter(person).send() }
addressBookOfFriends.each (envelope)

Закрытиеobject здесь значение переменной конверта и ее значенияuse является то, что это параметр для каждого метода.

Некоторые детали: Область действия. Область действия замыкания - это данные и члены, к которым можно получить доступ. Возврат из замыкания. Замыкания часто используют механизм обратного вызова для выполнения и возврата из самого себя. Аргументы: если замыканию требуется только 1 параметр, Groovy и другие языки предоставляют имя по умолчанию: "it", чтобы ускорить кодирование. Так, например, в нашем предыдущем примере:

addressBookOfFriends.each (envelope) 
is the same as:
addressBookOfFriends.each { new Letter(it).send() }

Надеюсь, это то, что вы ищете!

Замыкания не являются уникальными для функциональных языков. Они встречаются, например, в Паскале (и семье), который имеет вложенные процедуры. Стандарт С не имеет их (пока), но у IIRC есть расширение GCC.

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

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

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

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

Лямбда Python - простой пример функционального стиля ...

def parent () :
  a = "hello"
  return (lamda : a)

funcref = parent ()
print funcref ()

Мои питоны немного ржавые, но я думаю, что это правильно. Дело в том, что вложенная функция (лямбда) все еще ссылается на значение локальной переменнойa даже еслиparent вышел, когда это называется. Функция должна где-то сохранять это значение до тех пор, пока она не понадобится, и это место называется закрытием.

Закрытие немного похоже на неявный набор параметров.

Закрытие, это функция плюс состояние.

функция f является замыканием, когда оно закрывается (захвачено) x

что Питер Эдди правильно понял, но пример можно сделать более интересным. Вы могли бы определитьtwo функции, которые закрываются по локальной переменной, increment & amp; декремент. Счетчик будет разделен между этой парой функций и уникален для них. Если вы определите новую пару функций увеличения / уменьшения, они будут использовать другой счетчик.

Кроме того, вам не нужно передавать это начальное значение x, вы можете оставить его по умолчанию равным нулю внутри функционального блока. Это сделало бы более понятным, что оно использует значение, к которому у вас больше нет нормального доступа в противном случае.

Ваш ответ на вопрос