Jump to content

Анонимная рекурсия

В информатике , анонимная рекурсия — это рекурсия которая не вызывает явную функцию по имени. Это можно сделать либо явно, используя функцию более высокого порядка (передав функцию в качестве аргумента и вызвав ее), либо неявно, с помощью функций отражения , которые позволяют получить доступ к определенным функциям в зависимости от текущего контекста, особенно «текущей функции». " или иногда "вызывающая функция текущей функции".

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

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

Используйте [ править ]

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

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

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

Альтернативы [ править ]

Именованные функции [ править ]

Обычная альтернатива — использование именованных функций и именованной рекурсии. Учитывая анонимную функцию, это можно сделать либо путем привязки имени к функции, как в выражениях именованных функций в JavaScript, либо путем присвоения функции переменной и последующего вызова этой переменной, как в операторах функций в JavaScript. Поскольку языки, допускающие анонимные функции, обычно позволяют присваивать эти функции переменным (если не функциям первого класса), многие языки не предоставляют возможности ссылаться на саму функцию и явно отвергают анонимную рекурсию; примеры включают Go . [1]

Например, в JavaScript функция факториала может быть определена посредством анонимной рекурсии следующим образом: [2]

[1, 2, 3, 4, 5].map(function(n) {
     return (!(n > 1)) ? 1 : arguments.callee(n-1) * n;
});

Переписано для использования выражения именованной функции, что дает:

[1, 2, 3, 4, 5].map(function factorial(n) {
     return (!(n > 1)) ? 1 : factorial(n-1) * n;
});

Передача функций в качестве аргументов [ править ]

Даже без механизмов обращения к текущей функции или вызывающей функции анонимная рекурсия возможна в языке, который допускает функции в качестве аргументов. Это делается путем добавления еще одного параметра к базовой рекурсивной функции и использования этого параметра в качестве функции для рекурсивного вызова. Это создает функцию более высокого порядка, и передача этой функции более высокого порядка сама позволяет анонимную рекурсию внутри фактической рекурсивной функции. Это можно сделать чисто анонимно, применив к этой функции более высокого порядка комбинатор с фиксированной точкой . Это в основном представляет академический интерес, в частности, чтобы показать, что лямбда-исчисление имеет рекурсию, поскольку полученное выражение значительно сложнее, чем исходная названная рекурсивная функция. И наоборот, использование комбинаторов с фиксированной точкой можно в целом назвать «анонимной рекурсией», поскольку это их заметное использование, хотя у них есть и другие приложения. [3] [4]

Это показано ниже с использованием Python. Во-первых, стандартная именованная рекурсия:

def fact(n):
    if n == 0:
        return 1
    return n * fact(n - 1)

Использование функции более высокого порядка, чтобы функция верхнего уровня анонимно рекурсировала по аргументу, но при этом в качестве аргумента все равно требовалась стандартная рекурсивная функция:

def fact0(n0):
    if n0 == 0:
        return 1
    return n0 * fact0(n0 - 1)
fact1 = lambda f, n1: 1 if n1 == 0 else n1 * f(n1 - 1)
fact = lambda n: fact1(fact0, n)

Мы можем исключить стандартную рекурсивную функцию, передав аргумент функции в вызов:

fact1 = lambda f, n1: 1 if n1 == 0 else n1 * f(f, n1 - 1)
fact = lambda n: fact1(fact1, n)

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

F = lambda f: (lambda x: f(f, x))
fact1 = lambda f, n1: 1 if n1 == 0 else n1 * f(f, n1 - 1)
fact = F(fact1)

Написано анонимно: [5]

(lambda f: (lambda x: f(f, x))) \
(lambda g, n1: 1 if n1 == 0 else n1 * g(g, n1 - 1))

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

fact1 = lambda f: (lambda n1: 1 if n1 == 0 else n1 * f(f)(n1 - 1))
fact = fact1(fact1)

Здесь есть две операции «применения функции более высокого порядка к самой себе»: f(f) в первой строке и fact1(fact1) во втором. Вынесение второго двойного приложения в комбинатор дает:

C = lambda x: x(x)
fact1 = lambda f: (lambda n1: 1 if n1 == 0 else n1 * f(f)(n1 - 1))
fact = C(fact1)

Если исключить другое двойное приложение, получим:

C = lambda x: x(x)
D = lambda f: (lambda x: f(lambda v: x(x)(v)))
fact1 = lambda g: (lambda n1: 1 if n1 == 0 else n1 * g(n1 - 1))
fact = C(D(fact1))

Объединение двух комбинаторов в один дает комбинатор Y :

C = lambda x: x(x)
D = lambda f: (lambda x: f(lambda v: x(x)(v)))
Y = lambda y: C(D(y))
fact1 = lambda g: (lambda n1: 1 if n1 == 0 else n1 * g(n1 - 1))
fact = Y(fact1)

Раскрытие Y-комбинатора дает:

Y = lambda f: (lambda x: f(lambda v: x(x)(v))) \
              (lambda x: f(lambda v: x(x)(v)))
fact1 = lambda g: (lambda n1: 1 if n1 == 0 else n1 * g(n1 - 1))
fact = Y(fact1)

Их объединение дает рекурсивное определение факториала в лямбда-исчислении (анонимные функции одной переменной): [6]

(lambda f: (lambda x: f(lambda v: x(x)(v)))
           (lambda x: f(lambda v: x(x)(v)))) \
(lambda g: (lambda n1: 1 if n1 == 0 else n1 * g(n1 - 1)))

Примеры [ править ]

АПЛ [ править ]

В APL текущий dfn доступен через . Это позволяет анонимную рекурсию, например, в этой реализации факториала:

   {0=⍵:1  × -1} 5
120
   {0=⍵:1  × -1}¨ 10    ⍝ applied to each element of 0 to 9
1 1 2 6 24 120 720 5040 40320 362880

JavaScript [ править ]

В JavaScript текущая функция доступна через arguments.callee, а вызывающая функция доступна через arguments.caller. Они позволяют анонимную рекурсию, например, в этой реализации факториала: [2]

[1, 2, 3, 4, 5].map(function(n) {
    return (!(n > 1)) ? 1 : arguments.callee(n - 1) * n;
});

Перл [ править ]

Начиная с Perl 5.16, текущая подпрограмма доступна через __SUB__ токен, который возвращает ссылку на текущую подпрограмму, или undef вне подпрограммы. [7] Это позволяет анонимную рекурсию, например, в следующей реализации факториала:

#!/usr/bin/perl
use feature ":5.16";

print sub {
    my $x = shift;
    $x > 0
    ? $x * __SUB__->( $x - 1 )
    : 1;
}->(5), "\n";

Р [ править ]

В R текущую функцию можно вызвать с помощью Recall. Например,

sapply(0:5, function(n) {
  if (n == 0) return(1)
  n * Recall(n - 1)
})

Однако он не будет работать, если будет передан в качестве аргумента другой функции, например lapply, внутри определения анонимной функции. В этом случае, sys.function(0) можно использовать. [8] Например, приведенный ниже код рекурсивно возводит список в квадрат:

(function(x) {
  if (is.list(x)) {
    lapply(x, sys.function(0))
  } else {
    x^2
  }
})(list(list(1, 2, 3), list(4, 5)))

Ссылки [ править ]

  1. ^ Проблема 226: невозможно рекурсивно выполнить анонимную функцию в Go без обходных путей.
  2. ^ Jump up to: Перейти обратно: а б ответ olliej, 25 октября 2008 г. на вопрос « Почему свойство аргументов.callee.caller устарело в JavaScript? », StackOverflow
  3. ^ Эта терминология, по-видимому, во многом фольклорная , но она встречается в следующем:
    • Трей Нэш, Accelerated C# 2008 , Apress, 2007, ISBN   1-59059-873-3 , с. 462—463. В основном взято из Уэса Дайера (см. следующий пункт). блога
    • Анонимная рекурсия Уэса Дайера в C# , 2 февраля 2007 г., содержит по существу аналогичный пример, найденный в книге выше, но сопровождаемый дополнительным обсуждением.
  4. ^ The If Works , вывод Y-комбинатора , 10 января 2008 г.
  5. ^ Ответ Хьюго Уолтера на вопрос « Может ли лямбда-функция рекурсивно вызывать себя в Python? »
  6. ^ Ответ Nux на вопрос « Может ли лямбда-функция рекурсивно вызывать себя в Python? »
  7. ^ Perldoc, « Функция current_sub» , функция perldoc
  8. ^ ответ agstudy на получение вызываемой в данный момент функции для написания анонимной рекурсивной функции в StackOverflow
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: a7d95d166e2e822cf90166e42894e202__1671105240
URL1:https://arc.ask3.ru/arc/aa/a7/02/a7d95d166e2e822cf90166e42894e202.html
Заголовок, (Title) документа по адресу, URL1:
Anonymous recursion - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)