Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

Если вы хотите вызвать функцию рекурсивной стрелки, вам нужно вызвать комбинатор рекурсии со стрелкой в ​​качестве параметра, первый параметр функции стрелки - это рекурсивная функция, а остальные - параметры. Имя рекурсивной функции не имеет значения, поскольку оно не будет использоваться вне рекурсивного комбинатора. Затем вы можете вызвать функцию анонимной стрелки. Здесь мы вычисляем факториал 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))
                      );
    }
}

Ответ 3

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

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

Ответ 4

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

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

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

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) …

(уродливый, не так ли?)

Ответ 5

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

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

или строка reverse:

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

Ответ 6

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

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

Ответ 7

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

Будет ли это вариант?

Ответ 8

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

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

countdown(3)

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

Ответ 9

Это версия этого ответа fooobar.com/questions/57155/... с функциями стрелок.

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

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'))

Ответ 10

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

Итак, вы делаете факториальную функцию

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

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

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

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

(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 - это просто операция, которую вы выполняете для каждой рекурсии, например, умножение в Factorial или добавление в Фибоначчи: x1 => x2 => x1 + x2. и наконец nextStep - это следующее значение, которое передается в функцию, обычно текущее значение минус один: x => x - 1. Применять:

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

Ответ 11

я думаю, что самое простое решение - смотреть на единственное, чего у вас нет, а именно на ссылку на саму функцию. потому что, если у вас есть это, то отречение тривиально.

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

let generateTheNeededValue = (f, ...args) => f(f, ...args);

эта функция, как имя sugests, создаст ссылку, которая нам понадобится. теперь нам нужно только применить это к нашей функции

(generateTheNeededValue)(ourFunction, ourFunctionArgs)

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

let ourFunction = (me, ...ourArgs) => {...}

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

me(me, ...argsOnRecursion);

со всем этим. Теперь мы можем создать простую факториальную функцию.

((f, ...args) => f(f, ...args))((me, x) => {
  if(x < 2) {
    return 1;
  } else {
    return x * me(me, x - 1);
  }
}, 4)

-> 24

Мне также нравится смотреть на один лайнер этого

((f, ...args) => f(f, ...args))((me, x) => (x < 2) ? 1 : (x * me(me, x - 1)), 4)