Когда использовать закрытие? [закрыто]

Как-то вам нужно ссылаться на пространство имен cout std. Например, вставьте

using std::cout;
using std::endl;

поверх определения вашей функции или файла.

37
задан Community 23 May 2017 в 11:46
поделиться

7 ответов

Закрытия являются просто большими инструментами. Когда использовать их? Любое время Вам нравится... Как был уже сказан, альтернатива должна записать класс; например, пред C# 2.0, создавая параметризованный поток была реальная борьба. С C# 2.0 Вам даже не нужен 'ParameterizedThreadStart', который Вы просто делаете:

string name = // blah
int value = // blah
new Thread((ThreadStart)delegate { DoWork(name, value);}); // or inline if short

Сравнивают это с созданием класса с именем и оценивают

Или аналогично с поиском списка (использующий лямбду на этот раз):

Person person = list.Find(x=>x.Age > minAge && x.Region == region);

Снова - альтернатива должна была бы записать класс с двумя свойствами и методом:

internal sealed class PersonFinder
{
    public PersonFinder(int minAge, string region)
    {
        this.minAge = minAge;
        this.region = region;
    }
    private readonly int minAge;
    private readonly string region;
    public bool IsMatch(Person person)
    {
        return person.Age > minAge && person.Region == region;
    }
}
...
Person person = list.Find(new PersonFinder(minAge,region).IsMatch);

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

самый большой протест с получениями C# состоит в том, чтобы наблюдать объем; например:

        for(int i = 0 ; i < 10 ; i++) {
            ThreadPool.QueueUserWorkItem(delegate
            {
                Console.WriteLine(i);
            });
        }

Это не могло бы распечатать то, что Вы ожидаете, начиная с переменная я используюсь для каждого. Вы видели любую комбинацию повторений - даже 10 10-х. Необходимо тщательно определить объем полученных переменных в C#:

        for(int i = 0 ; i < 10 ; i++) {
            int j = i;
            ThreadPool.QueueUserWorkItem(delegate
            {
                Console.WriteLine(j);
            });
        }

Здесь каждый j получен отдельно (т.е. различный сгенерированный компилятором экземпляр класса).

у Jon Skeet есть хорошая запись в блоге, касающаяся C# и закрытий Java здесь ; или для большего количества детали, см. его книгу C# подробно , который имеет всю главу по ним.

32
ответ дан si618 23 May 2017 в 11:46
поделиться

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

Это так или иначе напоминает о:

почтенный основной королевский адвокат Na шел со своим студентом, Anton. Надеясь предложить ведущему устройству в обсуждение, Anton сказал "Ведущее устройство, я услышал, что объекты являются очень хорошей вещью - действительно ли это верно?" Королевский адвокат Na посмотрел жалостливо на своего студента и ответил, "Глупый ученик - объекты являются просто закрытиями бедного человека".

Отчитываемый, Anton взял свой отпуск от его ведущего устройства и возвратился к его ячейке, намерению учащихся закрытий. Он тщательно считал всю серию "Lambda: The Ultimate..." бумаг и ее кузенов, и реализовал маленький интерпретатор Схемы с основанной на закрытии объектной системой. Он изучил много и надеялся сообщить своему ведущему устройству его успеха.

На его следующем обходе с королевским адвокатом Na, Anton попытался произвести на свое ведущее устройство впечатление путем высказывания "Ведущего устройства, я старательно изучил вопрос, и теперь понимаю, что объекты являются действительно закрытиями бедного человека". Королевский адвокат Na ответил путем удара Anton его палкой, заявив, "Когда Вы будете учиться? Закрытия являются объектом бедного человека". В тот момент Anton стал просвещенным.

( http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html )

22
ответ дан Brian 23 May 2017 в 11:46
поделиться

Вот пример из стандартной библиотеки Python, inspect.py. Это в настоящее время читает

def strseq(object, convert, join=joinseq):
    """Recursively walk a sequence, stringifying each element."""
    if type(object) in (list, tuple):
        return join(map(lambda o, c=convert, j=join: strseq(o, c, j), object))
    else:
        return convert(object)

, Это имеет, как параметры, функция преобразования и функция соединения, и рекурсивно идет по спискам и кортежам. Рекурсия реализована с помощью карты (), где первый параметр является функцией. Код предшествует поддержке закрытий в Python, так нуждается в двух дополнительных параметрах по умолчанию, для передачи преобразовывают и присоединяются в рекурсивный вызов. С закрытиями это читает

def strseq(object, convert, join=joinseq):
    """Recursively walk a sequence, stringifying each element."""
    if type(object) in (list, tuple):
        return join(map(lambda o: strseq(o, convert, join), object))
    else:
        return convert(object)

На языках OO, Вы обычно не используете закрытия слишком часто, как можно использовать объекты передать состояние - и связанные методы, когда язык имеет их. Когда Python не имел закрытий, люди сказали, что Python эмулирует закрытия с объектами, тогда как Lisp эмулирует объекты с закрытиями. Как пример от НЕАКТИВНОГО (ClassBrowser.py):

class ClassBrowser: # shortened
    def close(self, event=None):
        self.top.destroy()
        self.node.destroy()
    def init(self, flist):
        top.bind("<Escape>", self.close)

Здесь, self.close является обратным вызовом без параметров, вызванным, когда Escape нажимается. Однако для близкой реализации действительно нужны параметры - а именно, сам, и затем self.top, self.node. Если бы Python не имел связанных методов, Вы могли бы записать

class ClassBrowser:
    def close(self, event=None):
        self.top.destroy()
        self.node.destroy()
    def init(self, flist):
        top.bind("<Escape>", lambda:self.close())

Здесь, лямбда добралась бы "сам" не от параметра, а от контекста.

3
ответ дан Martin v. Löwis 23 May 2017 в 11:46
поделиться

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

, Например, на языке как Lisp, можно определить функцию, которая возвращает функцию (с закрытым - по среде) для добавления некоторой предопределенной суммы к ее аргументу таким образом:

(defun make-adder (how-much)
  (lambda (x)
    (+ x how-much)))

и использование это как это:

cl-user(2): (make-adder 5)
#<Interpreted Closure (:internal make-adder) @ #x10009ef272>
cl-user(3): (funcall * 3)     ; calls the function you just made with the argument '3'.
8

На языке без закрытий, Вы сделали бы что-то вроде этого:

public class Adder {
  private int howMuch;

  public Adder(int h) {
    howMuch = h;
  }

  public int doAdd(int x) {
    return x + howMuch;
  }
}

и затем используют его как это:

Adder addFive = new Adder(5);
int addedFive = addFive.doAdd(3);
// addedFive is now 8.

закрытие неявно несет свою среду с ним; Вы беспрепятственно обращаетесь к той среде из выполняющейся части (лямбда). Без закрытий необходимо сделать ту среду явной.

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

можно реализовать объектную систему с закрытиями.

11
ответ дан Rich 23 May 2017 в 11:46
поделиться

В Lua и Python это - очень естественная вещь сделать "просто кодирование", потому что момент Вы ссылаетесь на что-то, которое это не параметр, при создании закрытия. (таким образом, большинство из них будет довольно тускло как примеры.)

Что касается конкретного случая, вообразите систему отмены/восстановления, где шаги являются парами (отмена (), восстановление ()) закрытия. Более громоздкие способы сделать, который мог бы быть к также: (a) Заставьте unredoable классы иметь специальный метод с универсально тупыми аргументами, или (b) разделите UnReDoOperation на подклассы многочисленные времена.

Другим конкретным примером являются бесконечные списки: Вместо того, чтобы работать с genericized контейнерами, Вы frob функция, которая получает следующий элемент. (это - часть питания итераторов.) В этом случае можно или сохранить просто немного состояния (следующее целое число для list-of-all-nonnegative-integers или подобный) или ссылка на положение в фактическом контейнере. Так или иначе это - функция, которая ссылается на что-то, что является вне себя. (в случае бесконечного списка переменные состояния должны быть переменными закрытия, потому что иначе они были бы чистыми для каждого вызова)

1
ответ дан Anders Eurenius 23 May 2017 в 11:46
поделиться

Самый простой пример использования закрытий находится в чем-то названном приправлением карри. В основном давайте предположим, что у нас есть функция f(), который, когда названо с двумя аргументами a и b, добавляет их вместе. Так, в Python мы имеем:

def f(a, b):
    return a + b

, Но скажем, ради аргумента, который мы только хотим назвать f() с одним аргументом за один раз. Так, вместо f(2, 3), мы хотим f(2)(3). Это может быть сделано как так:

def f(a):
    def g(b): # Function-within-a-function
        return a + b # The value of a is present in the scope of g()
    return g # f() returns a one-argument function g()

Теперь, когда мы звоним f(2), мы получаем новую функцию, g(); эта новая функция несет с ним переменные от объем из [1 111], и таким образом, это сказано [1 120] близкий [более чем 1 120] те переменные, следовательно термин закрытие. К тому, когда мы звоним g(3), переменная a (который связывается определением f), получают доступ [1 115], возвращаясь 2 + 3 => 5

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

def many_arguments(a, b, c, d, e, f, g, h, i):
    return # SOMETHING

def curry(function, **curry_args):
    # call is a closure which closes over the environment of curry.
    def call(*call_args):
        # Call the function with both the curry args and the call args, returning
        # the result.
        return function(*call_args, **curry_args)
    # Return the closure.
    return call

useful_function = curry(many_arguments, a=1, b=2, c=3, d=4, e=5, f=6)

useful_function теперь функция, для которой только нужны 3 аргумента, вместо 9. Я избегаю необходимости повторять меня, и также создал универсальный решение; если я пишу другую функцию много-аргумента, я могу использовать curry инструмент снова.

14
ответ дан zvoase 23 May 2017 в 11:46
поделиться

Эта статья включает два примера того, где замыкания действительно полезны: Closure

0
ответ дан 6 July 2019 в 20:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: