javascript: рекурсивна анонімна функція?


120

Скажімо, у мене є основна рекурсивна функція:

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();
})();

Мені б хотілося зателефонувати до функції, яка викликала цю функцію ... Я десь бачив сценарії (я не пам'ятаю, де), які можуть вказати вам назву функції, що називається, але я не можу згадати жодну з ця інформація прямо зараз.


Чи є причина, що вам це потрібно чи вам просто цікаво? Мені здається, було б зрозуміліше просто дати йому назву ...
rfunduk

1
@thenduks: З тієї ж причини, чому можна було б використовувати анонімну функцію. Тільки те, що іноді необхідна рекурсія.
ткнути

5
Сором arguments.calleeіснує, і це функціонування не робить нічого корисного. Я дивився на нього Y Combinator :P . Чорт, цей матеріал ніколи не стане корисним ...
Кобі

1
Так, як пов'язаний Кобі, використовуйте комбінатор з фіксованою точкою, такий як Y, щоб виконувати анонімні рекурсивні функції без аргументів.callee.
пароплав25

1
Див. W3future.com/weblog/stories/2002/02/22/… для прикладу комбінатора Y в JS.
пароплав25

Відповіді:


145

Ви можете надати функцію імені, навіть коли ви створюєте функцію як значення, а не оператор "декларація функції". Іншими словами:

(function foo() { foo(); })();

- рекурсивна функція, що продуває стек. Тепер, маючи на увазі , ви, мабуть, не хочете цього робити взагалі, оскільки є різні дивні проблеми з різними реалізаціями Javascript. ( зауважте - це досить старий коментар; деякі / багато / всі проблеми, описані в публікації блогу Kangax, можуть бути виправлені в більш сучасних браузерах.)

Коли ви даєте таке ім’я, воно не видно за межами функції (ну, цього не повинно бути; це одна з дивних ситуацій). Це як «летрек» у Ліспі.

Щодо arguments.calleeтого, це заборонено в "суворому" режимі і, як правило, вважається поганим, оскільки це робить деякі оптимізації важкими. Це також набагато повільніше, ніж можна було очікувати.

редагувати - Якщо ви хочете мати ефект "анонімної" функції, яка може телефонувати сама, ви можете зробити щось подібне (якщо припустити, що ви передаєте функцію як зворотний виклик чи щось подібне):

asyncThingWithCallback(params, (function() {
  function recursive() {
    if (timeToStop())
      return whatever();
    recursive(moreWork);
  }
  return recursive;
})());

Це означає, що визначити функцію з приємним, безпечним, не розбитим у IE заявою функції декларації , створюючи локальну функцію, назва якої не забруднює глобальний простір імен. Функція обгортки (справді анонімна) просто повертає цю локальну функцію.


Чи можемо ми уникнути забруднення глобального простору імен іншим способом за допомогою ES5 sctrict (я ще не читав глибоко в ES5)?
інкогніто

@pointy, чи можете ви подивіться на цей квест. stackoverflow.com/questions/27473450 / ...
Gladson Робінсону

Думаю, використовувати (() => { call_recursively_self_here() })()і називати себе рекурсивно не можна , правда? Я мушу дати йому ім’я.
Qwerty

1
@Qwerty добре, ви могли зробити щось подібне до останнього прикладу в моїй відповіді. Прив’яжіть функцію стрілки до локальної змінної у функції обгортки, щоб ваша функція стрілки могла називати себе ім'ям змінної. Потім обгортка повертає змінну (що стосується функції стрілки).
Pointy

1
@Pointy, можливо, деякі хакери знайдуть застосування;)
Kamil Kiełczewski

31

Люди говорили про комбінатор Y у коментарях, але ніхто не написав це як відповідь.

Комбінатор Y можна визначити у javascript таким чином: (спасибі парові25 за посилання)

var Y = function (gen) {
  return (function(f) {
    return f(f);
  }(function(f) {
    return gen(function() {
      return f(f).apply(null, arguments);
    });
  }));
}

І коли ви хочете передати свою анонімну функцію:

(Y(function(recur) {
  return function(data) {
    data = data+1;
    var nothing = function() {
      recur(data);
    }
    nothing();
  }
})());

Найважливіше, що слід зазначити щодо цього рішення, це те, що ви не повинні його використовувати.


16
"Найважливіше, що слід зазначити про це рішення, - це не використовувати його." Чому?
nyuszika7h

7
Це буде не швидко. Неприємно насправді використовувати (хоч і концептуально красиву!). Ви уникаєте присвоювати своїй функції тег або ім’я змінної (і я не бачу, чому це викликає занепокоєння), але ви все одно даєте їй ім'я як параметр зовнішньої функції, переданої Y. Отже, ви не здобути що-небудь, переживши всю цю неприємність.
зем

Не забудьте згадати, що ця функція не є безпечною. Цикловання всього пару тисяч разів призведе до переповнення стека.
Дякую

Привіт, я б запропонував трохи "чистішу" модифікацію, оскільки .apply (null, аргументи) мені здається некрасивим: var Y = function (gen) {return (function (f) {return f (f);} (function (f) {return gen (функція (x) {return f (f) (x);});})); } Або еквівалентно ((функція (x) {return y} дорівнює (x => y))), використовуючи позначення стрілки (дійсний код js): var Y = gen => (f => f (f)) (f = > gen (x => f (f) (x)))
myfirstAnswer

23

U комбінатор

Передаючи функцію собі як аргумент, функція може повторюватися, використовуючи свій параметр замість свого імені! Отже, функція, наданаU повинна мати принаймні один параметр, який буде прив'язуватися до функції (самої).

У наведеному нижче прикладі у нас немає умови виходу, тому ми просто циклічно будемо циклічно, поки не відбудеться переповнення стека

const U = f => f (f) // call function f with itself as an argument

U (f => (console.log ('stack overflow imminent!'), U (f)))

Ми можемо зупинити нескінченну рекурсію за допомогою різних методик. Тут я напишу нашу анонімну функцію, щоб повернути ще одну анонімну функцію, яка чекає на введення; у цьому випадку якась кількість. Коли число подається, якщо воно більше 0, ми продовжуватимемо повторюватися, інакше повернемо 0.

const log = x => (console.log (x), x)

const U = f => f (f)

// when our function is applied to itself, we get the inner function back
U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)
// returns: (x => x > 0 ? U (f) (log (x - 1)) : 0)
// where f is a reference to our outer function

// watch when we apply an argument to this function, eg 5
U (f => x => x > 0 ? U (f) (log (x - 1)) : 0) (5)
// 4 3 2 1 0

Тут не відразу видно, що наша функція при першому застосуванні до себе за допомогою Uкомбінатора повертає функцію, що чекає першого вводу. Якщо ми дамо ім'я цьому, можемо ефективно конструювати рекурсивні функції за допомогою лямбда (анонімні функції)

const log = x => (console.log (x), x)

const U = f => f (f)

const countDown = U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)

countDown (5)
// 4 3 2 1 0

countDown (3)
// 2 1 0

Тільки це не пряма рекурсія - функція, яка викликає себе за допомогою власного імені. Наше визначення countDownне посилається на внутрішнє тіло, і все ж можлива рекурсія

// direct recursion references itself by name
const loop = (params) => {
  if (condition)
    return someValue
  else
    // loop references itself to recur...
    return loop (adjustedParams)
}

// U combinator does not need a named reference
// no reference to `countDown` inside countDown's definition
const countDown = U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)

Як видалити самопосилання з існуючої функції за допомогою комбінатора U

Тут я покажу вам, як прийняти рекурсивну функцію, яка використовує посилання на себе і змінити її на функцію, яка використовує комбінатор U замість самопосилання

const factorial = x =>
  x === 0 ? 1 : x * factorial (x - 1)
  
console.log (factorial (5)) // 120

Тепер використовуйте комбінатор U для заміни внутрішнього посилання на factorial

const U = f => f (f)

const factorial = U (f => x =>
  x === 0 ? 1 : x * U (f) (x - 1))

console.log (factorial (5)) // 120

Основна схема заміни така. Зробіть ментальну записку, ми будемо використовувати аналогічну стратегію в наступному розділі

// self reference recursion
const foo =         x => ...   foo (nextX) ...

// remove self reference with U combinator
const foo = U (f => x => ... U (f) (nextX) ...)

Y комбінатор

пов'язано: комбінатори U і Y пояснюються за допомогою дзеркальної аналогії

У попередньому розділі ми бачили, як перетворити самореференційну рекурсію в рекурсивну функцію, яка не спирається на названу функцію за допомогою комбінатора U. Існує трохи роздратування, тому що потрібно пам'ятати, щоб завжди передавати функцію в якості першого аргументу. Ну, Y-комбінатор спирається на U-комбінатор і видаляє цей нудний шматочок. Це гарна річ, адже усунення / зменшення складності є основною причиною того, що ми робимо функції

Спочатку давайте виведемо власний Y-комбінатор

// standard definition
const Y = f => f (Y (f))

// prevent immediate infinite recursion in applicative order language (JS)
const Y = f => f (x => Y (f) (x))

// remove reference to self using U combinator
const Y = U (h => f => f (x => U (h) (f) (x)))

Тепер ми побачимо, як це використання порівняно з U-комбінатором. Зауважте, щоб повторити, замість цього U (f)ми можемо просто зателефонуватиf ()

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

Y (f => (console.log ('stack overflow imminent!'),  f ()))

Зараз я продемонструю countDownпрограму, використовуючи Y- ви побачите, що програми майже однакові, але комбінатор Y робить речі дещо чистішими

const log = x => (console.log (x), x)

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const countDown = Y (f => x => x > 0 ? f (log (x - 1)) : 0)

countDown (5)
// 4 3 2 1 0

countDown (3)
// 2 1 0

І тепер ми бачимо , factorialяк добре

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const factorial = Y (f => x =>
  x === 0 ? 1 :  x * f (x - 1))

console.log (factorial (5)) // 120

Як бачите, fсам механізм стає для рекурсії. Для повторення ми називаємо це як звичайна функція. Ми можемо називати це кілька разів різними аргументами, і результат все одно буде правильним. А оскільки це звичайний параметр функції, ми можемо називати його всім, що нам подобається, наприклад recurнижче -

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (recur => n =>
  n < 2 ? n : recur (n - 1) +  (n - 2))

console.log (fibonacci (10)) // 55


U і Y комбінатор з більш ніж 1 параметром

У наведених вище прикладах ми бачили, як ми можемо циклічно передавати аргумент та відслідковувати "стан" нашого обчислення. Але що робити, якщо нам потрібно вести облік додаткового стану?

Ми можемо використовувати складні дані, такі як масив чи щось таке ...

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (f => ([a, b, x]) =>
  x === 0 ? a : f ([b, a + b, x - 1]))

// starting with 0 and 1, generate the 7th number in the sequence
console.log (fibonacci ([0, 1, 7])) 
// 0 1 1 2 3 5 8 13

Але це погано, оскільки воно розкриває внутрішній стан (лічильники aта b). Було б добре, якби ми могли просто зателефонувати, fibonacci (7)щоб отримати потрібну відповідь.

Використовуючи те, що ми знаємо про викривлені функції (послідовності одинарних (1-параметр) функцій), ми можемо легко досягти своєї мети, не змінюючи наше визначення Y або покладаючись на складні дані або розширені особливості мови.

Подивіться на визначення fibonacciуважно нижче. Ми відразу ж застосування 0і 1які пов'язані з aі bвідповідно. Тепер Флор просто чекає подання останнього аргументу, до якого буде пов'язано x. Коли ми рекурсуємо, ми повинні викликати f (a) (b) (x)(не f (a,b,x)), оскільки наша функція знаходиться у викривленому вигляді.

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (f => a => b => x =>
  x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1)

console.log (fibonacci (7)) 
// 0 1 1 2 3 5 8 13


Такий зразок може бути корисним для визначення всіляких функцій. Нижче ми побачимо ще дві функції , визначені з допомогою Yкомбінатора ( rangeа reduce) і похідне reduce, map.

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const range = Y (f => acc => min => max =>
  min > max ? acc : f ([...acc, min]) (min + 1) (max)) ([])

const reduce = Y (f => g => y => ([x,...xs]) =>
  x === undefined ? y : f (g) (g (y) (x)) (xs))
  
const map = f =>
  reduce (ys => x => [...ys, f (x)]) ([])
  
const add = x => y => x + y

const sq = x => x * x

console.log (range (-2) (2))
// [ -2, -1, 0, 1, 2 ]

console.log (reduce (add) (0) ([1,2,3,4]))
// 10

console.log (map (sq) ([1,2,3,4]))
// [ 1, 4, 9, 16 ]


ВСІ АНОНИМНІ ОМГ

Оскільки тут ми працюємо з чистими функціями, ми можемо замінити будь-яку названу функцію для її визначення. Подивіться, що відбувається, коли ми візьмемо поле і замінимо названі функції їх виразами

/* const U = f => f (f)
 *
 * const Y = U (h => f => f (x => U (h) (f) (x)))
 *
 * const fibonacci = Y (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1)
 *
 */

/*
 * given fibonacci (7)
 *
 * replace fibonacci with its definition
 * Y (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
 *
 * replace Y with its definition
 * U (h => f => f (x => U (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
//
 * replace U with its definition
 * (f => f (f)) U (h => f => f (x => U (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
 */

let result =
  (f => f (f)) (h => f => f (x => h (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
  
console.log (result) // 13

І ось у вас це - fibonacci (7)обчислено рекурсивно, не використовуючи нічого, крім анонімних функцій


14

Використовувати "анонімний об'єкт" може бути найпростіше:

({
  do: function() {
    console.log("don't run this ...");
    this.do();
  }
}).do();

Ваш глобальний простір абсолютно не забруднений. Це досить просто. І ви можете легко скористатися неглобальним станом об'єкта.

Ви також можете використовувати об'єктні методи ES6, щоб зробити синтаксис більш стислим.

({
  do() {
    console.log("don't run this ...");
    this.do();
  }
}).do();

13

Я б не робив цього як вбудовану функцію. Це підштовхує до меж гарного смаку і насправді нічого не отримує від вас.

Якщо ви справді повинні, то є arguments.calleeвідповідь Фабріціо. Однак це, як правило, вважається недоцільним і заборонено в "суворому режимі" п'ятого видання ECMAScript. Незважаючи на те, що ECMA 3 і нестрогий режим не відходять, робота в суворому режимі обіцяє більше можливих оптимізацій мови.

Можна також використовувати іменовану вбудовану функцію:

(function foo(data){
    data++;
    var nothing = function() {
        foo(data);
    }
    nothing();
})();

Однак найкраще уникати також названих виразів вбудованих функцій, оскільки JScript IE робить для них деякі погані речі. У наведеному вище прикладі fooнеправильно забруднюється батьківська сфера в IE, і батьківський foo- окремий екземпляр до fooпобаченого всерединіfoo .

Яка мета ввести це у вбудовану анонімну функцію? Якщо ви просто хочете уникнути забруднення батьківської області, ви, звичайно, можете сховати свій перший приклад в іншій функції самовиклику-анонімності (простір імен). Вам справді потрібно створювати нову копію nothingкожного разу навколо рекурсії? Можливо, вам буде краще простору імен, що містить дві прості взаємно-рекурсивні функції.


Я погоджуюсь, що названа функція більше підходить, ніж argument.callee не тільки для суворого режиму ecmascript, але й для оптимізації, тому що при кожній рекурсії йому потрібно отримати посилання на виклику (і це, ймовірно, може знизити швидкість виконання )

+1 для поетичного, "pushing against the boundaries of good taste"- (ну і хороша інформація).
Пітер Айтай

як щодо простого pre / postfix, якщо забруднення насправді турбує тут? Зважаючи на те, що це не в глобальному масштабі (навіть якщо функція є найвищим рівнем lvl, у нього вже повинна бути анонімна функція, яка обгортає весь його код), це насправді малоймовірно, що ім’я на зразок recur_fooбуде стикатися з функцією в батьківській області (або бути хворим -використовується).
gblazex

Дуже цікаво - jsfiddle.net/hck2A - IE забруднює батьків у цьому випадку, як ви сказали. Ніколи цього не усвідомлював.
Пітер Айтай

1
@Peter: kangax.github.com/nfe (особливо "помилки JScript") більше, ніж ви коли-небудь хотіли знати з цього приводу. Він остаточно виправлений в IE9 (але лише в режимі IE9 Standards).
bobince

10
(function(data){
    var recursive = arguments.callee;
    data = data+1;
    var nothing = function() {
        recursive(data)
    }
    nothing();
})();

34
Я сподіваюсь, що всі, хто проголосував за цю (технічно правильну) відповідь, зрозуміли проблеми arguments.callee: це заборонено в суворому режимі та в ES5.
Pointy

Проголосовано, argument.callee застаріло в ES5
Хайме Родрігес

Він працює в NodeJS. Мені було б менше байдуже про ES5, якщо він працює передбачувано у визначених умовах.
Ангад

1
Це бомба за часом. Немає такого поняття, яке називається "фіксованим" середовищем, як підказує коментар вище. Ви майже завжди будете модернізувати через будь-яку з тисяч причин.
sampathsris

6

Ви можете зробити щось на кшталт:

(foo = function() { foo(); })()

або у вашому випадку:

(recur = function(data){
    data = data+1;
    var nothing = function() {
        if (data > 100) return; // put recursion limit
        recur(data);
    }
    nothing();
})(/* put data init value here */ 0);

Ви можете зробити це з оголошенням recurспочатку з varзаявою. Не знаю, чи це порушує правила питання, але, як у вас зараз, без varзаяви ви отримаєте помилку в жорсткому режимі ECMAScript 5.
Тім Даун

Мій початковий коментар включав varключове слово, але як тільки я перевірив цей код, він видавав помилки, оскільки ви не можете реально оголосити змінну всередині блоку, що викликає самовиклик, і мій підхід покладається на автоматичне декларування невизначеної змінної, а отже @ Pointy's рішення більш правильне. Але я все-таки проголосував за відповідь Фабріціо Кальдерана;)
ArtBIT

Так, робити (var recur = function() {...})();це не буде працювати, оскільки тепер це вираз, а не вираз призначення (який повертає присвоєне значення). Я var recur; (recur = function() {...})();натомість пропонував .
Тім Даун

3

Коли ви оголошуєте анонімну функцію на зразок цієї:

(function () {
    // Pass
}());

Його вважають виразом функції, і він має необов'язкове ім'я (яке ви можете використовувати для виклику його всередині себе. Але, оскільки це вираз функції (а не оператор), воно залишається анонімним (але має ім'я, яке ви можете викликати). ця функція може викликати себе:

(function foo () {
    foo();
}());
foo //-> undefined

"залишається анонімним" - ні, ні. Анонімна функція не має імені. Я розумію, що fooце не декларується в поточному контексті, але це більш-менш не має значення. Функція з іменем все ще називається функцією - не анонімною.
Дякую

3

Чому б не передати функцію самому функціонеру?

    var functionCaller = function(thisCaller, data) {
        data = data + 1;
        var nothing = function() {
            thisCaller(thisCaller, data);
        };
        nothing();
    };
    functionCaller(functionCaller, data);

3

У певних ситуаціях вам доводиться покладатися на анонімні функції. Дана рекурсивна mapфункція:

const map = f => acc => ([head, ...tail]) => head === undefined 
 ? acc
 : map (f) ([...acc, f(head)]) (tail);

const sqr = x => x * x;
const xs = [1,2,3,4,5];

console.log(map(sqr) ([0]) (xs)); // [0] modifies the structure of the array

Зверніть увагу, що mapне повинно змінювати структуру масиву. Тому акумулятор accне потрібно піддавати впливу. Ми можемо перейти mapдо іншої функції, наприклад:

const map = f => xs => {
  let next = acc => ([head, ...tail]) => head === undefined
   ? acc
   : map ([...acc, f(head)]) (tail);

  return next([])(xs);
}

Але це рішення досить багатослівне. Давайте скористаємось заниженим Uкомбінатором:

const U = f => f(f);

const map = f => U(h => acc => ([head, ...tail]) => head === undefined 
 ? acc
 : h(h)([...acc, f(head)])(tail))([]);

const sqr = x => x * x;
const xs = [1,2,3,4,5];

console.log(map(sqr) (xs));

Лаконічний, чи не так? Uмертвий просто, але має недолік, що рекурсивний виклик трохи затуманився: sum(...)стає h(h)(...)- ось і все.


2

Я не впевнений, чи потрібна відповідь, але це також можна зробити за допомогою делегатів, створених за допомогою function.bind:

    var x = ((function () {
        return this.bind(this, arguments[0])();
    }).bind(function (n) {
        if (n != 1) {
            return n * this.bind(this, (n - 1))();
        }
        else {
            return 1;
        }
    }))(5);

    console.log(x);

Це не передбачає іменованих функцій або argument.callee.


1

Як писав bobince, просто назвіть свою функцію.

Але, я здогадуюсь, ви також хочете перейти в початкове значення і врешті зупинити свою функцію!

var initialValue = ...

(function recurse(data){
    data++;
    var nothing = function() {
        recurse(data);
    }
    if ( ... stop condition ... )
        { ... display result, etc. ... }
    else
        nothing();
}(initialValue));

приклад роботи jsFiddle (використовує дані + = дані для розваги)



1
+1, Це дуже корисна відповідь, і ви повинні отримати більше інформації, але це не анонімно.
інкогніто

Ви чітко не прочитали, що написав Боббінс : However named inline function expressions are also best avoided.. Але ОП пропускає і сенс ... :)
gblazex

@Galamb - я читав. Заборонено в суворому режимі та в ES5 - це не те саме, що забруднює батьківський обсяг та створює зайві екземпляри.
Пітер Айтай

1

мені потрібна була (а точніше, хотілася) одноадресна анонімна функція, щоб пройти шлях до об'єкта, що створює рядок, і обробляла його так:

var cmTitle = 'Root' + (function cmCatRecurse(cmCat){return (cmCat == root) ? '' : cmCatRecurse(cmCat.parent) + ' : ' + cmCat.getDisplayName();})(cmCurrentCat);

який створює рядок типу "Root: foo: bar: baz: ..."


1

За допомогою ES2015 ми можемо трохи пограти з синтаксисом та зловживати параметрами та хитрощами за замовчуванням. Останні - це лише функції без жодних аргументів:

const applyT = thunk => thunk();

const fib = n => applyT(
  (f = (x, y, n) => n === 0 ? x : f(y, x + y, n - 1)) => f(0, 1, n)
);

console.log(fib(10)); // 55

// Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55...

Зверніть увагу, що fце параметр, який має анонімну функцію (x, y, n) => n === 0 ? x : f(y, x + y, n - 1)як значення за замовчуванням. Коли fвикликається applyTцим викликом має відбуватися без аргументів, так що використовується значення за замовчуванням. Значення за замовчуванням - це функція і, отже f, названа функція, яка може називати себе рекурсивно.


0

Ще одна відповідь, яка не передбачає названої функції або argument.callee

var sum = (function(foo,n){
  return n + foo(foo,n-1);
})(function(foo,n){
     if(n>1){
         return n + foo(foo,n-1)
     }else{
         return n;
     }
},5); //function takes two argument one is function and another is 5

console.log(sum) //output : 15

приємно: прив’яжіть анонімну функцію до локального параметра, а потім викликайте функцію через локальний параметр, але також передайте функцію собі для рекурсії.
englebart

0

Це переробка відповіді jforjs з різними іменами та дещо зміненим записом.

// function takes two argument: first is recursive function and second is input
var sum = (function(capturedRecurser,n){
  return capturedRecurser(capturedRecurser, n);
})(function(thisFunction,n){
     if(n>1){
         return n + thisFunction(thisFunction,n-1)
     }else{
         return n;
     }
},5); 

console.log(sum) //output : 15

Не потрібно було розгортати першу рекурсію. Функція, що отримує себе як еталон, повертається до споконвічної ооз OOP.


0

Це версія відповіді @ 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'))


0

Ще одне Y-комбінаторне рішення, використовуючи посилання rosetta-коду (я думаю, хтось раніше згадував про посилання десь у stackOverflow.

Стрілки для анонімних функцій для мене більш зрозумілі:

var Y = f => (x => x(x))(y => f(x => y(y)(x)));

-1

Це може працювати не скрізь, але ви можете використовувати arguments.calleeдля позначення поточної функції.

Отже, факториал можна зробити так:

var fac = function(x) { 
    if (x == 1) return x;
    else return x * arguments.callee(x-1);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.