Как написать функцию стрелки в ES6 рекурсивно?

функции стрелки в ES6 не имеют arguments собственность и поэтому arguments.callee не будет работать и в любом случае не будет работать в строгом режиме, даже если используется только анонимная функция.

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

Так... Как написать рекурсивную функцию стрелки? Это функция стрелки, которая рекурсивно называет себя на основе определенных условий и так далее, конечно?

10 ответов


написание рекурсивной функции без названия-это проблема, которая так же стара, как и сама компьютерная наука (даже старше, на самом деле, так как λ-исчисление предшествует информатике), так как в λ-исчислении все функции анонимны, и все же вам все еще нужна рекурсия.

решение состоит в использовании фиксированной точки. комбинатор, обычно у комбинатора. Это выглядит примерно так:

(y => 
  y(
    givenFact => 
      n => 
        n < 2 ? 1 : n * givenFact(n-1)
  )(5)
)(le => 
  (f => 
    f(f)
  )(f => 
    le(x => (f(f))(x))
  )
);

это вычислит факториал 5 рекурсивно.

Примечание: код сильно основан на этом:комбинатор Y объяснил с помощью JavaScript. Все заслуга автора. Я в основном просто "гармонизирован" (это то, что вы называете рефакторингом старого кода с новыми функциями из ES/Harmony?) он.


Клаус Рейнке дал ответ на ваш вопрос в обсуждении esdiscuss.org сайт.

в ES6 вы должны определить, что он называет комбинатором рекурсии.

 let rec = (f)=> (..args)=> f( (..args)=>rec(f)(..args), ..args )

Если вы хотите вызвать рекурсивную функцию стрелки, вы должны вызвать комбинатор рекурсии с функцией стрелки в качестве параметра, первый параметр функции стрелки-рекурсивная функция, а остальные-параметры. Имя рекурсивной функции не имеет важность, поскольку она не будет использоваться вне рекурсивного комбинатора. Затем можно вызвать функцию anonymous arrow. Здесь мы вычисляем факториал 6.

 rec( (f,n) => (n>1 ? n*f(n-1) : n) )(6)

Если вы хотите протестировать его в Firefox, вам нужно использовать перевод ES5 комбинатора рекурсии:

function rec(f){ 
    return function(){
        return f.apply(this,[
                               function(){
                                  return rec(f).apply(this,arguments);
                                }
                            ].concat(Array.prototype.slice.call(arguments))
                      );
    }
}

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

var complex = (a, b) => {
    if (a > b) {
        return a;
    } else {
        complex(a, b);
    }
};

используйте переменную, которой вы назначаете функцию, например

const fac = (n) => n>0 ? n*fac(n-1) : 1;

Если вам действительно нужно это анонимно, используйте Y комбинатор, например:

const Y = (f) => ((x)=>f((v)=>x(x)(v)))((x)=>f((v)=>x(x)(v)))
… Y((fac)=>(n)=> n>0 ? n*fac(n-1) : 1) …

(некрасиво, не так ли?)


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

const rec = (le => ((f => f(f))(f => (le((...x) => f(f)(...x))))));

это может быть использовано, например, для определения факториала:

const factorial = rec( fact => (n => n < 2 ? 1 : n * fact(n - 1)) );
//factorial(5): 120

или обратный строки:

const reverse = rec(
  rev => (
    (w, start) => typeof(start) === "string" 
                ? (!w ? start : rev(w.substring(1), w[0] + start)) 
                : rev(w, '')
  )
);
//reverse("olleh"): "hello"

или в порядке обхода дерева:

const inorder = rec(go => ((node, visit) => !!(node && [go(node.left, visit), visit(node), go(node.right, visit)])));

//inorder({left:{value:3},value:4,right:{value:5}}, function(n) {console.log(n.value)})
// calls console.log(3)
// calls console.log(4)
// calls console.log(5)
// returns true

С arguments.callee плохой вариант из-за устаревания/не работает в строгом режиме и делает что-то вроде var func = () => {} также плохо, это хак, как описано в этом ответе, вероятно, ваш единственный вариант:

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


var rec = () => {rec()};
rec();

Это был бы вариант?


вы можете назначить свою функцию переменной внутри iife

var countdown = f=>(f=a=>{
  console.log(a)
  if(a>0) f(--a)
})()

countdown(3)

//3
//2
//1
//0

это версия этого ответа,https://stackoverflow.com/a/3903334/689223, с функциями стрелки.

можно использовать U или Y комбинатора. Y combinator является самым простым в использовании.

U combinator, с этим вы должны продолжать передавать функцию: const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))

Y combinator, с этим вам не нужно продолжать передавать функцию: const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))


я нашел предоставленные решения действительно сложными и честно не мог понять ни одного из них, поэтому я сам придумал более простое решение (я уверен, что это уже известно, но здесь идет мой мыслительный процесс):

таким образом, Вы делаете факториальную функцию

x => x < 2 ? x : x * (???)

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

f => x => x < 2 ? x : x * f(x-1)

это не сработало. потому что когда мы зовем f(x-1) мы называем себя эту функцию, и мы только что определили ее аргументы как 1) f: сама функция, опять же и 2)x значение. Ну, у нас есть сама функция, f помнишь? так что просто пройти его первым:

f => x => x < 2 ? x : x * f(f)(x-1)
                            ^ the new bit

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

(f => x => x < 2 ? x : x * f(f)(x-1))(f => x => x < 2 ? x : x * f(f)(x-1))(5)
>120

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

y => y(y)

и передайте ему свою факториальную функцию создания:

(y => y(y))(f => x => x < 2 ? x : x * f(f)(x-1))(5)
>120

бум. Вот небольшая формула:

(y => y(y))(f => x => endCondition(x) ? default(x) : operation(x)(f(f)(nextStep(x))))

для основной функции, которая добавляет числа от 0 до x, endCondition это когда вам нужно прекратить повторяться, так что x => x == 0. default это последнее значение, которое вы даете один раз endCondition удовлетворено, так x => x. operation это просто операция, которую вы делаете на каждой рекурсии, например, умножение в Факториале или добавление в Фибоначчи:x1 => x2 => x1 + x2. и, наконец,nextStep - следующее значение для передачи функции, которое обычно является текущим значением минус один:x => x - 1. Применить:

(y => y(y))(f => x => x == 0 ? x : x + f(f)(x - 1))(5)
>15