Основное преднамеренное различие в семантике между основными языками состоит в том, разрешать ли изменения переменных, захваченных замыканием. Java и Python говорят «нет», другие языки говорят «да» (ну, я не знаю Objective C, но остальные знают). Преимущество возможности изменять переменные состоит в том, что вы можете писать код следующим образом:
public static Func<int,int> adderGen(int start) {
return (delegate (int i) { // <-- start is captured by the closure
start += i; // <-- and modified each time it's called
return start;
});
}
// later ...
var counter = adderGen(0);
Console.WriteLine(counter(1)); // <-- prints 1
Console.WriteLine(counter(1)); // <-- prints 2
// :
// :
Вы заметите, что это намного меньше кода, чем эквивалентный класс счетчика, хотя C # (язык, используемый здесь) генерирует именно этот код для вас за кулисами.
Недостатком является то, что захваченные переменные действительно являются общими, поэтому, если вы сгенерируете несколько сумматоров в классическом цикле for, вас ждет сюрприз ...
var adders = new List<Func<int,int>>();
for(int start = 0; start < 5; start++) {
adders.Add(delegate (int i) {
start += i;
return start;
});
}
Console.WriteLine(adders[0](1)); // <-- prints 6, not 1
Console.WriteLine(adders[4](1)); // <-- prints 7, not 5
Мало того, что start
является общим для всех 5 замыканий, повторное start++
дает ему значение 5 в конце цикла for. В языке смешанной парадигмы мое мнение заключается в том, что Java и Python имеют правильную идею - если вы хотите изменить захваченные переменные, вам лучше вместо этого создать класс, который делает захват Процесс явный, когда вы передаете их конструктору, например. Мне нравится хранить замыкания для функционального программирования.
Кстати, у Perl тоже есть замыкания.