Что такое вложенные функции? Для чего они? - PullRequest
2 голосов
/ 06 февраля 2009

Я никогда не использовал вложенные функции, но видел ссылки на них на нескольких языках (а также на вложенные классы, которые, как я предполагаю, связаны).

  • Что такое вложенная функция?
  • Почему?!? * * 1006
  • Что вы можете сделать с вложенной функцией, которую вы не можете сделать другим способом?
  • Что вы можете сделать с вложенной функцией, это сложно или не элегантно без вложенных функций?

Я предполагаю, что вложенные функции - это просто артефакт обработки всего как объекта, и если объекты могут содержать другие объекты, то это следует.

Имеют ли вложенные функции область видимости (в общем, я полагаю, языки отличаются в этом) так же, как переменные внутри функции имеют область видимости?

Пожалуйста, добавьте язык, на который вы ссылаетесь, если вы не уверены, что ваш ответ не зависит от языка.

-Adam

Ответы [ 7 ]

4 голосов
/ 06 февраля 2009

Одно из популярных применений вложенных функций - замыкания . На языке с лексической областью действия с первоклассными функциями можно использовать функции для хранения данных. Простым примером в Scheme является счетчик:

(define (make-counter)
  (let ((count 0))                ; used to store the count
    (define (counter)             ; this is the counter we're creating
      (set! count (+ count 1))    ; increment the count
      count)                      ; return the new count
    counter))                     ; return the new counter function

(define mycounter (make-counter)) ; create a counter called mycounter

(mycounter)                       ; returns 1

(mycounter)                       ; returns 2

В этом примере мы вкладываем счетчик функций в функцию make-counter, и, возвращая эту внутреннюю функцию, мы получаем доступ к данным, доступным для счетчика, когда он был определен. Эта информация является частной для данного экземпляра mycounter - если бы мы создали другой счетчик, он бы использовал другое место для хранения внутреннего счетчика. Продолжая предыдущий пример:

(define mycounter2 (make-counter))

(mycounter2)                      ; returns 1

(mycounter)                       ; returns 3
2 голосов
/ 06 февраля 2009

Вложенная функция - это просто функция внутри другой функции.

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

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

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

2 голосов
/ 06 февраля 2009

Это полезно для рекурсии, когда есть только 1 метод, который когда-либо будет вызывать его

string[] GetFiles(string path)
{
  void NestedGetFiles(string path, List<string> result)
  {
    result.AddRange( files in the current path);
    foreach(string subPath in FoldersInTheCurrentPath)
      NestedGetFiles(subPath, result);
  }

   List<string> result = new List<string>();
   NestedGetFiles(path, result);
   return result.ToArray();
}

Приведенный выше код полностью составлен, но основан на C #, чтобы дать представление о том, что я имею в виду. Единственный метод, который может вызывать NestedGetFiles, - это метод GetFiles.

1 голос
/ 06 февраля 2009

Вложенная функция - это просто функция, определенная в теле другой функции. Зачем? Единственная причина, по которой я мог придумать, - это вспомогательная или служебная функция.

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

function process(qryResult q1, qryResult q2) {

   object o;
   if (q1.someprop == "useme") {
       o.prop1 = q1.prop1;
       o.prop2 = q1.prop2;
       o.prop3 = q1.prop3;
   } else if (q2.someprop == "useme") {
       o.prop1 = q2.prop1;
       o.prop2 = q2.prop2;
       o.prop3 = q2.prop3;
   }

   return o;

}

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

function process(qryResult q1, qryResult q2) {

   object o;
   if (q1.someprop == "useme") {
       fillObject(o,q1);
   } else if (q2.someprop == "useme") {
       fillObject(o,q2);
   }

   return o;

   function fillObject(object o, qryResult q) {
       o.prop1 = q.prop1;
       o.prop2 = q.prop2;
       o.prop3 = q.prop3;
   }


}

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

1 голос
/ 06 февраля 2009

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

>>> def GetIntMaker(x):
...   def GetInt():
...     return x
...   return GetInt
... 
>>> GetInt = GetIntMaker(1)
>>> GetInt()
1
1 голос
/ 06 февраля 2009

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

int doStuff() {
    int result;
    void cleanUpReturn() {
        myResource1.release();
        myResource2.release();
        return result * 2 + 1;
    }

    auto myResource1 = getSomeResource();
    auto myResource2 = getSomeOtherResource();
    if(someCondition) {
        return cleanUpReturn();
    } else {
        doSomeOtherStuff();
        return cleanUpReturn();
    }
}

Конечно, в этом случае это также может быть обработано с помощью RAII, но это всего лишь простой пример.

1 голос
/ 06 февраля 2009

(C #): Я использую это, чтобы упростить представление Object Browser и лучше структурировать мои классы. Как класс Колесо вложено в Грузовой класс.

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

...