Answer
t.me/js_testРешений два
const seq1 = ( x, $ = console.log( ( seq1(x - 1, x > 1 ? void x : x), x ) ) ) => null; const seq2 = ( x, $ = x > 1 ? seq2(x - 1) : void x, _ = console.log(x) ) => null;
Принцип работы
Идея решения использует особенности того, как вычисляются функции, для которых определены аргументы по-умолчанию. Если у функции они есть, то при функция при вызове будет вычислена в два прохода:
- в первый проход будут вычислены значения инициализаторов для аргументов, у которых переданное значение отсутствует или равно
undefined; - во второй проход будет вычислено само тело функции;
Во втором проходе имена из первого прохода станут доступны как родительское пространство имен.
Это означает, что функция такого вида:
function foo(arg1, arg2 = do_something(), arg3 = do_whatever(arg2)) {
return [ arg1, arg2, arg3 ];
}
является синтаксическим сахаром для следующей равноценной функции:
function foo(#arg1, #arg2, #arg3) {
let arg1 = #arg1;
let arg2 = #arg2 !== undefined ? #arg2 : do_something();
let arg3 = #arg3 !== undefined ? #arg3 : do_whatever(arg2);
return (() => {
return [ arg1, arg2, arg3 ];
})();
}
Изнутри кода, вычисляющего инициализаторы, сама функция уже доступна по имени, поэтому может быть вызвана рекурсивно.
Подробнее про это поведение можно почитать на MDN и в спецификации ECMAScript.
Объяснение
Решение можно построить как минимум двумя путями — явно выйдя из рекурсии по собственному условию; или неявно выйдя из рекурсии, основываясь на том, что инициализатор не вычисляется для аргумента со значением, отличным от undefined.
Первое решение рассахаривается в следующую конструкцию:
const seq1 = (_x, _$) => {
let x = _x;
let $ = _$ !== undefined ? _$ : console.log(
seq1(x - 1, x > 1 ? void x : x),
x
)
return (() => null)();
}
Что, в свою очередь, упрощается до:
const seq1 = (x, stopNow) => {
if (!stopNow) {
if (x > 1) {
seq1(x - 1, true);
} else {
seq1(x - 1);
}
console.log(x);
}
return null;
}
Т.е. мы входим в рекурсию всегда, но если вторым аргументом передан флаг "хватит уже", то мы сразу же из нее выходим.
Второе решение рассахаривается в следующий код:
const seq2 = (_x, _$, __) => {
let x = _x;
let $ = _$ !== undefined ? _$ : (x > 1 ? seq2(x - 1) : void x);
let _ = __ !== undefined ? __ : console.log(x);
return (() => null)();
}
Что, в свою очередь, упрощается до:
const seq2 = (x) => {
if (x > 1) {
seq2(x - 1);
}
console.log(x);
return null;
}
В отличие от первого решения, мы даже не пробуем входить в рекурсию, если условие не выполняется.
Код для проверки
const seq1 = (
x,
$ = console.log( (
seq1(x - 1, x > 1 ? void x : x),
x
) )
) => null;
const seq2 = (
x,
$ = x > 1 ? seq2(x - 1) : void x,
_ = console.log(x)
) => null;
seq1(5); // 1 2 3 4 5
console.log("---");
seq2(5); // 1 2 3 4 5