Что такое вложенные функции? Для чего они?

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

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

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

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

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

-Адам


person Adam Davis    schedule 06.02.2009    source источник


Ответы (7)


Одним из популярных способов использования вложенных функций является замыкания. В языке с лексической областью действия и первоклассными функциями это можно использовать функции для хранения данных. Простым примером в 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
person Kyle Cronin    schedule 06.02.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.

person Peter Morris    schedule 06.02.2009

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

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

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

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

person rz.    schedule 06.02.2009

(C#): я использую это, чтобы упростить представление обозревателя объектов и лучше структурировать свои классы. Поскольку класс Wheel вложен в класс Truck.

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

person wiwulo    schedule 06.02.2009

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

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, но это всего лишь простой пример.

person dsimcha    schedule 06.02.2009

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

>>> def GetIntMaker(x):
...   def GetInt():
...     return x
...   return GetInt
... 
>>> GetInt = GetIntMaker(1)
>>> GetInt()
1
person Jason Baker    schedule 06.02.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;
   }


}

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

person DMKing    schedule 06.02.2009