Скажем, у меня есть базовая рекурсивная функция:
function recur(data) {
data = data+1;
var nothing = function() {
recur(data);
}
nothing();
}
Как я мог это сделать, если у меня есть анонимная функция, например ...
(function(data){
data = data+1;
var nothing = function() {
//Something here that calls the function?
}
nothing();
})();
Мне нужен способ вызвать функцию, которая вызвала эту функцию ... Я где-то видел сценарии (не помню, где), которые могут сообщить вам имя вызываемой функции, но я не могу вспомнить ни одного из эта информация прямо сейчас.
javascript
recursion
scope
anonymous-function
Инкогнито
источник
источник
arguments.callee
, что эта функция не делает ничего полезного. Я смотрел на него Y Combinator:P
. Черт, эта штука никогда не пригодится ...Ответы:
Вы можете дать функции имя, даже если вы создаете функцию как значение, а не оператор «объявления функции». Другими словами:
является рекурсивной функцией сдува стека. С учетом сказанного,
возможно, вывообще незахотите этого делать, потому что существуют некоторые странные проблемы с различными реализациями Javascript. ( обратите внимание - это довольно старый комментарий; некоторые / многие / все проблемы, описанные в сообщении блога Kangax, могут быть исправлены в более современных браузерах.)Когда вы даете такое имя, оно не отображается вне функции (ну, этого не должно быть; это одна из странностей). Это как "letrec" в Лиспе.
Что касается
arguments.callee
, это запрещено в «строгом» режиме и обычно считается плохим, потому что это затрудняет некоторые оптимизации. К тому же это намного медленнее, чем можно было ожидать.edit - Если вы хотите получить эффект «анонимной» функции, которая может вызывать сама себя, вы можете сделать что-то вроде этого (при условии, что вы передаете функцию как обратный вызов или что-то в этом роде):
При этом определяется функция с красивым, безопасным, не нарушенным в IE оператором объявления функции , создавая локальную функцию, имя которой не будет загрязнять глобальное пространство имен. Функция-оболочка (действительно анонимная) просто возвращает эту локальную функцию.
источник
(() => { call_recursively_self_here() })()
и вызывать себя рекурсивно, верно? Я должен дать ему имя.Люди говорили о комбинаторе Y в комментариях, но никто не написал его в качестве ответа.
Комбинатор Y можно определить в javascript следующим образом: (спасибо steamer25 за ссылку)
И когда вы хотите передать свою анонимную функцию:
Самое важное, что следует отметить в этом решении, - это то, что вы не должны его использовать.
источник
U комбинатор
Передавая функцию самой себе в качестве аргумента, функция может повторяться, используя свой параметр вместо имени! Таким образом, данная функция
U
должна иметь хотя бы один параметр, который будет привязан к самой функции.В приведенном ниже примере у нас нет условия выхода, поэтому мы будем просто бесконечно зацикливаться, пока не произойдет переполнение стека.
Мы можем остановить бесконечную рекурсию, используя различные методы. Здесь я напишу нашу анонимную функцию, чтобы возвращать другую анонимную функцию, ожидающую ввода; в данном случае какое-то количество. При вводе числа, если оно больше 0, мы продолжим повторение, иначе вернем 0.
Что не сразу очевидно, так это то, что наша функция при первом применении к самой себе с помощью
U
комбинатора возвращает функцию, ожидающую первого ввода. Если бы мы дали этому имя, можно эффективно создавать рекурсивные функции, используя лямбды (анонимные функции).Только это не прямая рекурсия - функция, которая вызывает себя по собственному имени. Наше определение
countDown
не ссылается на себя внутри своего тела, но рекурсия все же возможна.Как удалить ссылку на себя из существующей функции с помощью U-комбинатора
Здесь я покажу вам, как взять рекурсивную функцию, которая использует ссылку на себя, и изменить ее на функцию, которая использует комбинатор U вместо ссылки на себя.
Теперь, используя комбинатор U для замены внутренней ссылки на
factorial
Основная схема замены такова. Обратите внимание, мы будем использовать аналогичную стратегию в следующем разделе.
Y комбинатор
В предыдущем разделе мы увидели, как преобразовать рекурсию со ссылками на себя в рекурсивную функцию, которая не полагается на именованную функцию, используя U-комбинатор. Немного раздражает необходимость не забывать всегда передавать функцию самой себе в качестве первого аргумента. Что ж, Y-комбинатор основан на U-комбинаторе и устраняет этот утомительный элемент. Это хорошо, потому что устранение / уменьшение сложности - основная причина, по которой мы делаем функции.
Во-первых, давайте выведем наш собственный Y-комбинатор
Теперь посмотрим, как его использование сравнивается с U-комбинатором. Обратите внимание, чтобы повторить, вместо того,
U (f)
чтобы просто позвонитьf ()
Теперь я продемонстрирую использование
countDown
программыY
- вы увидите, что программы почти идентичны, но комбинатор Y делает вещи немного чище.И теперь мы видим ,
factorial
как хорошоКак видите,
f
сам механизм рекурсии становится. Повторюсь, мы называем это как обычную функцию. Мы можем вызывать его несколько раз с разными аргументами, и результат все равно будет правильным. А поскольку это обычный параметр функции, мы можем назвать его как угодно, например,recur
ниже -Комбинатор U и Y с более чем 1 параметром
В приведенных выше примерах мы увидели, как можно зацикливаться и передавать аргумент, чтобы отслеживать «состояние» наших вычислений. Но что, если нам нужно отслеживать дополнительное состояние?
Мы могли бы использовать составные данные, такие как массив или что-то в этом роде ...
Но это плохо, потому что выявляет внутреннее состояние (счетчики
a
иb
). Было бы неплохо, если бы мы могли просто позвонить,fibonacci (7)
чтобы получить нужный нам ответ.Используя то, что мы знаем о каррированных функциях (последовательностях унарных (с одним параметром) функций), мы можем легко достичь нашей цели, не изменяя наше определение
Y
или полагаться на составные данные или расширенные языковые функции.fibonacci
Внимательно посмотрите на определение слова ниже. Сразу же подаем заявку0
и1
которые привязаны кa
иb
соответственно. Теперь фибоначчи просто ждет, когда будет предоставлен последний аргумент, к которому будет привязанx
. При рекурсии мы должны вызыватьf (a) (b) (x)
(неf (a,b,x)
), потому что наша функция находится в каррированной форме.Такой вид шаблона может быть полезен для определения всех видов функций. Ниже мы увидим еще две функции , определенные с помощью
Y
комбинатора (range
аreduce
) и производноеreduce
,map
.ЭТО ВСЕ АНОНИМНОЕ OMG
Поскольку здесь мы работаем с чистыми функциями, мы можем заменить ее определение любой именованной функцией. Посмотрите, что происходит, когда мы берем фибоначчи и заменяем названные функции их выражениями
И вот оно -
fibonacci (7)
вычислено рекурсивно с использованием только анонимных функций.источник
Вместо этого может быть проще использовать «анонимный объект»:
Ваше глобальное пространство полностью незагрязнено. Это довольно просто. И вы можете легко воспользоваться неглобальным состоянием объекта.
Вы также можете использовать методы объекта ES6, чтобы сделать синтаксис более лаконичным.
источник
Я бы не стал делать это как встроенную функцию. Это выходит за рамки хорошего вкуса и ничего вам не дает.
Если вам действительно нужно, есть
arguments.callee
как в ответе Фабрицио. Однако обычно это считается нецелесообразным и запрещено в «строгом режиме» ECMAScript Fifth Edition. Хотя ECMA 3 и нестрогий режим никуда не денутся, работа в строгом режиме обещает больше возможных оптимизаций языка.Также можно использовать именованную встроенную функцию:
Однако лучше избегать именованных встроенных функциональных выражений, поскольку IE JScript делает с ними некоторые плохие вещи. В приведенном выше примере
foo
неправильно загрязняет родительскую область видимости в IE, и родительскийfoo
объект является отдельным экземпляром дляfoo
видимого внутриfoo
.Какова цель использования встроенной анонимной функции? Если вы просто хотите избежать загрязнения родительской области видимости, вы, конечно, можете скрыть свой первый пример внутри другой самовызывающейся анонимной-функции (пространства имен). Вам действительно нужно создавать новую копию
nothing
каждый раз вокруг рекурсии? Возможно, вам будет лучше использовать пространство имен, содержащее две простые взаимно рекурсивные функции.источник
"pushing against the boundaries of good taste"
- (ну и хорошая инфа).recur_foo
будет конфликтовать с функцией в родительской области (или быть больным -используемый) .источник
arguments.callee
: он запрещен в строгом режиме и в ES5.Вы можете сделать что-то вроде:
или в вашем случае:
источник
recur
сначала объявить с помощьюvar
утверждения. Не знаю, нарушает ли это правила вопроса, но, поскольку у вас это есть сейчас, безvar
оператора вы получите ошибку в строгом режиме ECMAScript 5.var
ключевое слово, но как только я протестировал этот код, он вызывал ошибки, так как вы не можете действительно объявить переменную внутри самовызывающегося блока, а мой подход основан на автоматическом объявлении переменной undefined, и, следовательно, @ Pointy's решение более правильное. Но я все же проголосовал за ответ Фабрицио Кальдерана;)(var recur = function() {...})();
не сработает, поскольку теперь это оператор, а не выражение присваивания (которое возвращает присвоенное значение). Я предлагалvar recur; (recur = function() {...})();
вместо этого.Когда вы объявляете анонимную функцию следующим образом:
Он считается функциональным выражением и имеет необязательное имя (которое вы можете использовать для вызова его изнутри самого себя. Но поскольку это выражение функции (а не оператор), оно остается анонимным (но имеет имя, которое вы можете вызвать). эта функция может вызывать сама себя:
источник
foo
это не объявляется в текущем контексте, но это более или менее не имеет значения. Функция с именем остается именованной функцией, а не анонимной.Почему бы не передать функцию самой функции?
источник
В определенных ситуациях вам придется полагаться на анонимные функции. Дана рекурсивная
map
функция:Обратите внимание, что
map
нельзя изменять структуру массива. Так что аккумуляторacc
выставлять не нужно. Мы можем, например, перейтиmap
к другой функции:Но это решение довольно многословное. Воспользуемся заниженным
U
комбинатором:Лаконично, не правда ли?
U
чрезвычайно прост, но имеет тот недостаток, что рекурсивный вызов немного запутывается:sum(...)
становитсяh(h)(...)
- вот и все.источник
Я не уверен, что ответ по-прежнему требуется, но это также можно сделать с помощью делегатов, созданных с помощью function.bind:
Это не касается именованных функций или arguments.callee.
источник
Как написал bobince, просто назовите свою функцию.
Но я предполагаю, что вы также хотите передать начальное значение и в конечном итоге остановить свою функцию!
рабочий пример jsFiddle (использует данные + = данные для развлечения)
источник
However named inline function expressions are also best avoided.
. Но OP тоже упускает из виду ... :)Мне нужна была (или, скорее, хотелось) однострочная анонимная функция, которая двигалась бы вверх по объекту, формируя строку, и обработал ее следующим образом:
что создает строку типа 'Root: foo: bar: baz: ...'
источник
В ES2015 мы можем немного поиграть с синтаксисом и злоупотреблять параметрами и преобразователями по умолчанию. Последние - это просто функции без аргументов:
Обратите внимание, что
f
это параметр с анонимной функцией в(x, y, n) => n === 0 ? x : f(y, x + y, n - 1)
качестве значения по умолчанию. Когдаf
вызываетсяapplyT
этим вызовом, должно происходить без аргументов, чтобы использовалось значение по умолчанию. Значение по умолчанию - это функция и, следовательно,f
именованная функция, которая может вызывать себя рекурсивно.источник
Другой ответ, который не включает названную функцию или аргументы. Callee
источник
Это переработанный ответ jforjs с другими именами и немного измененной записью.
Первую рекурсию разворачивать не пришлось. Функция, принимающая себя в качестве ссылки, восходит к исконной массе ООП.
источник
Это версия ответа @zem со стрелочными функциями.
Вы можете использовать комбинатор
U
илиY
. Y-комбинатор - самый простой в использовании.U
комбинатор, при этом вы должны продолжать передавать функцию:const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))
Y
комбинатор, при этом вам не нужно постоянно передавать функцию:const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))
источник
Еще одно решение Y-комбинатора, использующее ссылку на розетт -код (я думаю, что кто-то ранее упоминал ссылку где-то в stackOverflow.
Стрелки для анонимных функций более читабельны:
источник
Это может работать не везде, но вы можете использовать его
arguments.callee
для ссылки на текущую функцию.Итак, факториал можно сделать так:
источник