Указатели функции, Закрытия и Лямбда

84
задан Carmen SC 14 March 2018 в 17:55
поделиться

10 ответов

Лямбда (или закрытие ) инкапсулирует и указатель функции и переменные. Поэтому в C# можно сделать:

int lessThan = 100;
Func<int, bool> lessThanTest = delegate(int i) {
   return i < lessThan;
};

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

int lessThan = 100;
Func<int, bool> lessThanTest = delegate(int i) {
   return i < lessThan;
};

lessThanTest(99); // returns true
lessThan = 10;
lessThanTest(99); // returns false

В C, это было бы недопустимо:

BOOL (*lessThanTest)(int);
int lessThan = 100;

lessThanTest = &LessThan;

BOOL LessThan(int i) {
   return i < lessThan; // compile error - lessThan is not in scope
}

, хотя я мог определить указатель функции, который берет 2 аргумента:

int lessThan = 100;
BOOL (*lessThanTest)(int, int);

lessThanTest = &LessThan;
lessThanTest(99, lessThan); // returns true
lessThan = 10;
lessThanTest(100, lessThan); // returns false

BOOL LessThan(int i, int lessThan) {
   return i < lessThan;
}

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

, Хотя большинство основных языков, которые поддерживают закрытия, использует анонимные функции, нет никакого требования для этого. У Вас могут быть закрытия без анонимных функций и анонимных функций без закрытий.

Сводка: закрытие является комбинацией указателя функции + полученные переменные.

107
ответ дан Mark Brackett 24 November 2019 в 08:32
поделиться

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

0
ответ дан SpaceghostAli 24 November 2019 в 08:32
поделиться

Основное различие является результатом отсутствия лексического обзора в C.

указатель функции А просто что, указатель на блок кода. Любая переменная нестека, на которую это ссылается, глобальна, статична или подобна.

закрытие А, OTOH, имеет свое собственное состояние в форме 'внешних переменных' или 'upvalues'. они могут быть столь частными или общими, как Вы хотите, с помощью лексического обзора. Можно создать много закрытий с тем же функциональным кодом, но различные экземпляры переменных.

Несколько закрытий могут совместно использовать некоторые переменные, и так могут быть интерфейсом объекта (в смысле ООП). для создания этого в C, необходимо связать структуру с таблицей указателей функции (это - то, что C++ делает с vtable классом).

короче говоря, закрытие является указателем функции ПЛЮС некоторое состояние. это - высокоуровневая конструкция

1
ответ дан Javier 24 November 2019 в 08:32
поделиться

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

{
    my $count;
    sub increment { return $count++ }
}

закрытие является средой, которая определяет $count переменная. Это только доступно increment подпрограмма и сохраняется между вызовами.

1
ответ дан Community 24 November 2019 в 08:32
поделиться

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

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

я не удобная лямбда приравнивания с закрытием, потому что я не уверен, что лямбды на всех языках являются закрытиями, время от времени я думаю, что лямбды только что были локально определенными анонимными функциями без привязки переменных (Python пред 2,1?).

2
ответ дан Andy Dent 24 November 2019 в 08:32
поделиться

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

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

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

вверх funargs проблема является более трудным. GCC не препятствует тому, чтобы Вы позволили указателю батута существовать после того, как внешняя функция больше не активна (не имеет никакой записи на стеке вызовов), и затем статический указатель ссылки мог указать на мусор. Записи активации больше не могут выделяться на стеке. Обычное решение состоит в том, чтобы выделить их на "куче" и позволить функциональному объекту, представляющему вложенную функцию, просто указывают на запись активации внешней функции. Такой объект называют закрытие . Тогда язык должен будет обычно поддерживать сборка "мусора" так, чтобы записи могли быть освобождены, как только больше нет указателей, указывающих на них.

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

4
ответ дан Jouni K. Seppänen 24 November 2019 в 08:32
поделиться

Закрытие = логика + среда.

, Например, рассмотрите этот метод C# 3:

public Person FindPerson(IEnumerable<Person> people, string name)
{
    return people.Where(person => person.Name == name);
}

лямбда-выражение не только инкапсулирует логику ("сравнивают имя"), но также и среда, включая параметр (т.е. локальная переменная) "имя".

Для больше на этом, взгляните на мой статья о закрытиях , который берет Вас через C# 1, 2 и 3, показывая, как закрытия делают вещи легче.

6
ответ дан Jon Skeet 24 November 2019 в 08:32
поделиться

В C Вы не можете определить встроенную функцию, таким образом, Вы не можете действительно создать закрытие. Все, что Вы делаете, раздает ссылку на некоторый предопределенный метод. На языках, которые поддерживают анонимные методы/закрытия, определение методов намного более гибко.

Простым языком, указатели функции не имеют никакого объема, связанного с ними (если Вы не считаете глобальную область видимости), тогда как закрытия включают объем метода, это определяет их. С лямбдами можно записать метод, который пишет метод. Закрытия позволяют Вам связывать "некоторые аргументы функции и получению функции более низкой арности в результате". (взятый из комментария Thomas). Вы не можете сделать этого в C.

РЕДАКТИРОВАНИЕ: Добавление примера (я собираюсь использовать причину синтаксиса Actionscript-выхода, это - то, что находится на моем уме прямо сейчас):

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

function runLater(f:Function):Void {
  sleep(100);
  f();
}

Теперь говорят, что Вы хотите пользователю runLater () задержать некоторую обработку объекта:

function objectProcessor(o:Object):Void {
  /* Do something cool with the object! */
}

function process(o:Object):Void {
  runLater(function() { objectProcessor(o); });
}

функция Вы являетесь передающими для обработки (), больше не некоторая статически определенная функция. Это динамично сгенерировано и в состоянии включать ссылки на переменные, которые были в объеме, когда метод был определен. Так, это может получить доступ к 'o' и 'objectProcessor', даже при том, что те не находятся в глобальной области видимости.

я надеюсь тот имевший смысл.

8
ответ дан Pacerier 24 November 2019 в 08:32
поделиться

Поскольку кто-то, кто записал компиляторы для языков и с и без 'реальных' закрытий, я почтительно, не соглашается с некоторыми ответами выше. Lisp, Схема, ML или закрытие Haskell не создают новую функцию динамично . Вместо этого это повторные использования существующая функция , но делает так с [1 119] новые свободные переменные . Набор свободных переменных часто называют среда , по крайней мере, теоретики языка программирования.

закрытие А является просто агрегатом, содержащим функцию и среду. В Стандартном ML компилятора Нью-Джерси мы представили тот как запись; одно поле содержало указатель на код, и другие поля содержали значения свободных переменных. Компилятор создал новое закрытие (не функция) динамично путем выделения новой записи, содержащей указатель на тот же код, но с отличающийся значения для свободных переменных.

можно моделировать все это в C, но это - боль в заднице. Два метода популярны:

  1. Передача указатель на функцию (код) и отдельный указатель на свободные переменные, так, чтобы закрытие было разделено через две переменные C.

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

Техника № 1 идеальна, когда Вы пытаетесь моделировать некоторый полиморфизм в C, и Вы не хотите показывать тип среды---, Вы используете пустоту* указатель для представления среды. Для примеров, взгляд на Dave Hanson Интерфейсы C и Реализации . Техника № 2, который более тесно напоминает то, что происходит в компиляторах собственного кода для функциональных языков, также напоминает другую знакомую технику... Объекты C++ с виртуальными функциями членства. Реализации почти идентичны.

Это наблюдение, ведомое к шпильке от Henry Baker:

Люди в мире Алгола/Фортрана жаловались в течение многих лет, что не поняли то, что закрытия функции возможного применения будут иметь в эффективном программировании будущего. Тогда оборот 'объектно-ориентированного программирования' произошел, и теперь все программируют закрытия функции использования, за исключением того, что они все еще отказываются для вызова их этим.

41
ответ дан Norman Ramsey 24 November 2019 в 08:32
поделиться

Лямбда является анонимным, динамично определил функция. Вы просто не можете сделать этого в C... что касается закрытий (или convination двух), типичный пример шепелявости посмотрел бы что-то вроде:

(defun get-counter (n-start +-number)
     "Returns a function that returns a number incremented
      by +-number every time it is called"
    (lambda () (setf n-start (+ +-number n-start))))

В терминах C, Вы могли сказать, что лексическая среда (стек) get-counter получается анонимной функцией и изменяется внутренне как следующие шоу в качестве примера:

[1]> (defun get-counter (n-start +-number)
         "Returns a function that returns a number incremented
          by +-number every time it is called"
        (lambda () (setf n-start (+ +-number n-start))))
GET-COUNTER
[2]> (defvar x (get-counter 2 3))
X
[3]> (funcall x)
5
[4]> (funcall x)
8
[5]> (funcall x)
11
[6]> (funcall x)
14
[7]> (funcall x)
17
[8]> (funcall x)
20
[9]> 
3
ответ дан dsm 24 November 2019 в 08:32
поделиться
Другие вопросы по тегам:

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