Как написать функцию стрелки в 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 = () => {}
также плохо, это хак, как описано в этом ответе, вероятно, ваш единственный вариант:
вы можете назначить свою функцию переменной внутри 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