Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Получить имя функции внутри функции не используя callee (https://javascript.ru/forum/misc/79421-poluchit-imya-funkcii-vnutri-funkcii-ne-ispolzuya-callee.html)

drwhite 05.02.2020 20:51

Получить имя функции внутри функции не используя callee
 
Приветствую, камрады, можно ли сделать как в PHP:
public function func($in_params)
{ 
    $__F__ = __FUNCTION__;
    ...
    if (...) $__F__($in_params);
}

чтобы не зависеть от имени функции, но не используя callee?

Vlasenko Fedor 05.02.2020 22:02

Пример варианта для window
<script>
function test(name) {
    if (typeof window[name] === "function") {
        window[name]();
    } else {
        alert("Function not declared")
    }
}

function one() {
    alert('One');
}

test("one");
test("two");

</script>

Malleys 06.02.2020 03:40

Poznakomlus, пишут ведь, что «чтобы не зависеть от имени функции». Ещё ваш вариант предполагает засорение глобального объекта, а также его изменение! Также у вас не соблюдена чистота функции. Также вы не показали, как получить функцию внутри функции не используя callee!

drwhite, в JavaScript как раз для этого и есть arguments.callee, который указывает на функцию.

Однако стоит учитывать, что по имени функции не всегда возможно обратиться к самой функции, поэтому лучше говорить о ссылке на функцию.
function factorial(n) {
    if(n % 1 !== 0 || n < 0) throw Error("n ∈ Z ⋀ n ≥ 0");
    if(n === 0) return 1;
    return n * arguments.callee(n - 1);
}

alert(factorial(5)); // 5! = 120


Однако, если вы вдруг решили, что вам по каким-то причинам arguments.callee не подходит (по каким?), то вы можете использовать Y-комбинатор!

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

var factorial = Y(fn => n => {
    if(n % 1 !== 0 || n < 0) throw Error("n ∈ Z ⋀ n ≥ 0");
    if(n === 0) return 1;
    return n * fn(n - 1);
});

alert(factorial(5)); // 5! = 120
Обращение функции к самой себе вообще не предполагает наличие глобальной ссылки. Т. е. поскольку она не называет себя по имени, она не связана тесно с самой собой.

рони 06.02.2020 08:39

:write: может я что-то не понимаю, но альтернативы arguments.callee в решениях выше Poznakomlus и
Malleys
не увидел.

Aetae 06.02.2020 10:29

Потому что её нет.
Если вы работаете в нестрогом режиме - у вас уже есть arguments.callee, никакого смысла в чём-то ещё нет.
Если вы работаете в строгом режиме - arguments.callee у вас отобрали специально, чтобы заметно ускорить и облегчить исполнение избавившись от сложных взаимосвязей и побочек. Альтернативы, соответственно, тут принципиально не будет.

drwhite 06.02.2020 10:48

Цитата:

Сообщение от Malleys
Однако, если вы вдруг решили, что вам по каким-то причинам arguments.callee не подходит (по каким?)

Именно потому, что оно устаревшее и вообще я за стрикт моде.

Цитата:

Сообщение от Malleys
можете использовать Y-комбинатор!

«— Будете у нас на Колыме…
— нет, уж лучше вы к нам» =)

Цитата:

Сообщение от Aetae
в строгом режиме - arguments.callee у вас отобрали специально, чтобы заметно ускорить и облегчить исполнение избавившись от сложных взаимосвязей и побочек

Но ведь так удобно не зависеть от имени функции при рекурсии. Чем, кстати, оно может нагадить?

Malleys 06.02.2020 11:10

Цитата:

Сообщение от рони
альтернативы arguments.callee в решениях выше Poznakomlus и
Malleys не увидел.

В моём втором примере показазан Y-комбинатор.

Цитата:

Сообщение от Aetae
Альтернативы, соответственно, тут принципиально не будет.

Цитата:

Сообщение от drwhite
Но ведь так удобно не зависеть от имени функции при рекурсии.

Y-комбинатор делает именно то, что вы хотите. Или что вы имеете в виду, когда пишете «не зависеть от имени функции при рекурсии»?

рони 06.02.2020 11:18

Цитата:

Сообщение от Malleys
Y-комбинатор делает именно то, что вы хотите. Или что вы имеете в виду, когда пишете «не зависеть от имени функции при рекурсии»?

для меня Y-комбинатор, выглядит как вместо имени функции будем использовать имя функции

drwhite 06.02.2020 12:08

Цитата:

Сообщение от Malleys
Y-комбинатор делает именно то, что вы хотите. Или что вы имеете в виду, когда пишете «не зависеть от имени функции при рекурсии»?

хочу как-то так:
function func($params)
{
    ...
    if (...) self($params);
}


чтобы имя функции указывалось один раз, без лишних переменных, без NFE и в strict mode =)

Vlasenko Fedor 06.02.2020 12:38

<script>
function test() {
    try {
        throw  new Error( "Get name")
    } catch (e) {
        console.log(e.stack);
    }
}
test();
</script>

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

Malleys 06.02.2020 13:00

Цитата:

Сообщение от Poznakomlus
название функции есть в стеке вызовов, однако этот стек по разному работает в броузерах

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

Цитата:

Сообщение от drwhite
хочу как-то так:
function func($params)
{
    ...
    if (...) self($params);
}

А откуда должен браться этот self? Например в браузере уже есть self обозначающий текущий глобальный объект. Чистота функции подразумевает, что вы должны явно указать все используемые аргументы, т. е. иммутабельное глобальное окружение.

А значит вам нужно самим указать аргумент и это правильно...
function func(self, $params)
{
    ...
    if (...) self($params);
}
И это использует имя функции только один раз, нет лишних переменных, есть strict mode и чистота функции. Вопрос только в том, как её запустить!

drwhite 06.02.2020 13:08

Цитата:

Сообщение от Poznakomlus
название функции есть в стеке вызовов

Рекурсия через исключения?
Это даже покруче, чем eval)

drwhite 06.02.2020 13:12

Цитата:

Сообщение от Malleys
А откуда должен браться этот self?

в моем примере "self" — это псевдокод, ссылка на функцию, аналог __FUNCTION__ в пхп. Неужто в JS нет такой возможности? Не верю)

Malleys 06.02.2020 13:19

Цитата:

Сообщение от drwhite
в моем примере "self" — это псевдокод, ссылка на функцию, аналог __FUNCTION__ в пхп. Неужто в JS нет такой возможности? Не верю)

В JS это arguments.callee, но вы не можете это использовать!

Цитата:

Сообщение от рони
для меня Y-комбинатор, выглядит как вместо имени функции будем использовать имя функции

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

Рассмотрим рекурсивную функцию...
const factorial = x => {
	if (x === 1)
		return 1;
	else
		return x * factorial(x - 1);
}

alert(factorial(5));
Она сейчас не удовлетворяет понятию чистоты функции, поскольку её результат зависит не только от входных параметров.

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

(myself, x) => {
	if (x === 1)
		return 1;
	else
		return x * myself(myself, x - 1);
}
Эта функция работает только со своими аргументами.

Для функции, написанной в такой форме, можно написать такую функцию recursive, которая превратит нашу функцию в рекурсивную функцию.

const recursive = fn => (...args) => fn(fn, ...args);

const factorial = recursive(
	(myself, x) => {
		if (x === 1)
			return 1;
		else
			return x * myself(myself, x - 1);
	}
);

alert(factorial(5));


Поскольку теперь рекурсивная функция отделена от самой себя, мы можем сделать что-то вроде запоминания её значении:

const recursive = fn => (...args) => fn(fn, ...args);

const memoized = (fn, keymaker = JSON.stringify) => {
	const cache = {};

	return function(...args) {
		const key = keymaker.call(this, args);
		return cache[key] || (cache[key] = fn.apply(this, args));
	}
};

const skipFirst = ([_, ...values]) => JSON.stringify(values);

const factorial = recursive(
	memoized(
		(myself, x) => {
			if (x === 1)
				return 1;
			else
				return x * myself(myself, x - 1);
		},
		skipFirst
	)
);

alert(factorial(5));


Запоминание нашей рекурсивной функцей не требует каких-либо изменений в её коде. Это можно легко использовать в другом месте, если нужно.

Почему recursive нуждается в улучшении?

recursive — полезная функция, но у неё есть недостаток: в дополнение к переписыванию наших функций, чтобы взять себя в качестве параметра, мы также должны переписать их, чтобы они передавались сами собой. Итак, в дополнение к этому...
(myself, x) => ...
Мы также должны написать это...
myself(myself, x-1)
Если первое указывает, что используется, то последнее ерунда!

Нам нужна такая функция, как recursive, но она должна поддерживать функции, вызывающие себя так myself(x-1).

Давайте представим, что именно мы хотим. С recursive мы пишем:
const factorial = recursive(
	(myself, x) => {
		if (x === 1)
			return 1;
		else
			return x * myself(myself, x - 1);
	}
);


Нам нужен лучший рекурсивный комбинатор (ниже обозначен как ???), который позволит нам написать...
const factorial = ???(
	(myself, x) => {
		if (x === 1)
			return 1;
		else
			return x * myself(x - 1);
	}
);


Давайте сделаем!

Прежде чем мы начнем, есть несколько правил, которым мы должны следовать, если мы хотим переделать recursive в улучшенный комбинатор. Каждый комбинатор обладает следующими свойствами:
  • Это функция.
  • Он может использовать только свои параметры или ранее определенные комбинаторы, которые имеют такие же свойства. Это означает, что он не может ссылаться на не-комбинаторы — такие как константы, обычные функции или объекты из глобального пространства имен.
  • Он не может быть объявлением именованной функции или выражением именованной функции.
  • Он не может создать выражение с именованной функцией.
  • Он не может объявлять никаких переменных и констант, кроме того, что получено через параметры.
  • Он может вызывать функцию в своей реализации.
  • В комбинаторной логике все комбинаторы принимают ровно один параметр, как и все функции, которые создают комбинаторы.
  • Комбинаторная логика также избегает деструктуризации аргументов и применения полученного путём деструкткризации массива параметров к другой функции.

Давайте начнём с нашего объявленного выше комбинатора...
const recursive = fn => (...args) => fn(fn, ...args);


Во-первых, назовём его betterRecursive
const betterRecursive =
	fn =>
		(...args) =>
			fn(fn, ...args);


Во-вторых, обозначим изменение, которое хотим сделать...
const betterRecursive =
	fn =>
		(...args) =>
			fn(?, ...args);


Мы заменили fn на ? Почему? Потому что наша функция fn выглядит следующим образом: (myself, arg0, arg1, ..., argn) => .... Но мы хотим опустить myself, что выглядит следующим образом: (arg0, arg1, ..., argn) => .... Значит, это не может быть fn.

Но что тогда?

Давайте подумаем о функции recursive. Что она делает? Она принимает функцию (myself, arg0, arg1, ..., argn) => ... и возвращает функцию, которая выглядит следующим образом (arg0, arg1, ..., argn) => ....

Функция recursive — не подходит, но давайте предположим, что такая подходящяя функция существует. Мы назовем её maker, потому что она делает функцию, которую мы хотим.

Значит далее, шаг третий, мы заменим ? на maker(??). Мы знаем, что maker(??) делает функцию, которую мы хотим, но мы ещё не знаем, что мы должны передать ей:
const betterRecursive =
	fn =>
		(...args) =>
			fn(maker(??), ...args);


Нужно выяснить откуда мы получаем maker и какие параметры мы передаем ему.

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

const betterRecursive =
	fn => (
		maker =>
			(...args) =>
				fn(maker(??), ...args)
	)(???)


maker — это функция, которая принимает один или несколько параметров и возвращает функцию, которая выглядит следующим образом (...args) => fn(maker(??), ...args). Это функция, которую мы хотим передать функции fn как myself. Также как и в функции recursive, тут мы видим в коде выше такую функцию maker => (...args) => fn(maker(??), ...args), которая принимает один параметр (maker) и возвращает функцию, которая выглядит как (...args) => fn(maker(??), ...args)!

const betterRecursive =
	fn => (
		maker =>
			(...args) =>
				fn(maker(??), ...args)
	)(
		maker =>
			(...args) =>
				fn(maker(??), ...args)
	)


Функция maker принимает один или несколько параметров и возвращает функцию, которая выглядит как (...args) => fn(maker(??), ...args)

maker => (...args) => fn(maker(??), ...args) — это функция, которая принимает один параметр (maker) и возвращает функцию, которая выглядит следующим образом (...args) => fn(maker(??), ...args).

Отсюда вывод — maker принимает один параметр maker и возвращает функцию, которая выглядит следующим образом (...args) => fn(maker(??), ...args). Поэтому выражение, которое нам нужно — maker(maker), а значит ?? является не более чем функцией maker!

const betterRecursive =
	fn => (
		maker =>
			(...args) =>
				fn(maker(maker), ...args)
	)(
		maker =>
			(...args) =>
				fn(maker(maker), ...args)
	)


Давайте проверим...
const betterRecursive =
	fn => (
		maker =>
			(...args) =>
				fn(maker(maker), ...args)
	)(
		maker =>
			(...args) =>
				fn(maker(maker), ...args)
	);

const factorial = betterRecursive(
	(myself, x) => {
		if (x === 1)
			return 1;
		else
			return x * myself(x - 1);
	}
);

alert(factorial(5));

Вуаля! Рабочая betterRecursive! drwhite, это именно то, что вы хотели!

Malleys 06.02.2020 13:43

Функция betterRecursive, о которой упоминалось выше, написана на JavaScript, где функции могут принимать более одного параметра. Формально комбинатор принимает только один параметр и работает только с функциями, которые принимают только один параметр.

Мы можем перевести нашу функцию betterRecursive в формальный комбинатор, Y-комбинатор. Чтобы было понятней, давайте сначала представим рекурсивную функцию:

const isEven =
  n =>
    (n === 0) || !isEven(n - 1);


Функция, которая принимается функцией betterRecursive

const _isEven =
  (myself, n) =>
    (n === 0) || !myself(n - 1);


Увы, сейчас она принимает два параметра. Можно исправить, применив каррирование...

const __isEven =
  myself =>
    n =>
      (n === 0) || !myself(n - 1);

Вместо того, чтобы принимать два параметра (myself и n), теперь эта функция, которая принимает один параметр myself и возвращает функцию, которая принимает другой параметр n.

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

const Y =
  fn =>
    (
      maker =>
        (...args) =>
          fn(maker(maker), ...args)
    )(
      maker =>
        (...args) =>
          fn(maker(maker), ...args)
    );

Далее мы видим, что (...args) => fn(maker(maker), ...args) недопустимо, мы не используем деструктуризацию аргументов и применение полученного путём деструкткризации массива параметров к другой функции. Во-первых, мы изменяем ...args на arg, поскольку разрешен только один параметр:

const Y =
  fn =>
    (
      maker =>
        arg => fn(maker(maker), arg)
    )(
      maker =>
        arg => fn(maker(maker), arg)
    );


fn(maker(maker), arg) также не допускается, мы не передаем два параметра любой функции. Вместо этого мы передаем один параметр, получаем функцию обратно и передаем второй параметр этой функции....

const Y =
  fn =>
    (
      maker =>
        arg => fn(maker(maker))(arg)
    )(
      maker =>
        arg => fn(maker(maker))(arg)
    );


Или с более краткими именами, вариант, который упоминался ранее...
const Y =
	f => (
		g =>
			x => f(g(g))(x)
	)(
		g =>
			x => f(g(g))(x)
	);

Давай попробуем:

const Y =
	f => (
		g =>
			x => f(g(g))(x)
	)(
		g =>
			x => f(g(g))(x)
	);
	
alert(Y(
	myself =>
		n =>
			(n === 0) || !myself(n - 1)
)(1962));


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

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

Также вы можете использовать https://www.npmjs.com/package/y-combinator

Alexandroppolus 06.02.2020 13:44

Цитата:

Сообщение от drwhite
чтобы имя функции указывалось один раз, без лишних переменных, без NFE и в strict mode =)

какова вообще задача? то есть, нафига нужен такой изыск?

Vlasenko Fedor 06.02.2020 13:50

Цитата:

Сообщение от drwhite
Рекурсия через исключения?
Это даже покруче, чем eval)

любую рекурсию можно заменить циклом

рони 06.02.2020 13:50

Malleys,
возможно и я когда нибудь пойму, что вы здесь написали ... :) :( :( :(

Alexandroppolus 06.02.2020 14:03

Цитата:

Сообщение от рони (Сообщение 519707)
Malleys,
возможно и я когда нибудь пойму, что вы здесь написали ... :) :( :( :(

функциональщина выносит мозг, это да)))

здесь надо просто взять вызов Y(...) и размотать в обратную сторону, тогда будет понятно, что и как вызывается

Malleys 06.02.2020 14:24

Цитата:

Сообщение от Alexandroppolus
функциональщина выносит мозг, это да)))

И про машину Тьюринга можно так сказать и про лямбда-исчисление Алонзо Черча. Просто вы привыкли к тем языкам, которые ведут свое начало от машины Тьюринга, поэтому другая часть языков, которая идёт от лямбда-исчисления Алонзо Черча кажется вам нелогичной и запутанной.

Цитата:

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

Я думаю, саму идею, для чего и как его использовать вы поняли... Вот, например, сортировка...

/* библиотека комбинаторов */
var Y =
	f => (
		g =>
			x => f(g(g))(x)
	)(
		g =>
			x => f(g(g))(x)
	);

/* программа */
var qsort = sort => array => {
	if(array.length <= 1) return array;

	return [
		...sort(array.filter(item => item  <  array[0])),
		     ...array.filter(item => item === array[0]),
		...sort(array.filter(item => item  >  array[0]))
	];
};

alert(Y(qsort)([4,13,2,12,3,11,1,10,5,9,6,8,7]))

рони 06.02.2020 14:42

Цитата:

Сообщение от Malleys
Я думаю, саму идею, для чего и как его использовать вы поняли... Вот, например, сортировка...

ценю ваш тонкий юмор.

drwhite 06.02.2020 17:20

Цитата:

Сообщение от Malleys
Вуаля! Рабочая betterRecursive! drwhite, это именно то, что вы хотели!

Благодарю, все верно, но так хотелось сделать это в одну строчку кода)

Цитата:

Сообщение от Alexandroppolus
какова вообще задача? то есть, нафига нужен такой изыск?

Примерно такая же, как и в случае с this и __construct для класса — не надо явно использовать имя, которое может взять и поменяться.
Может быть это и изыск, но, согласитесь, удобнее)

Vlasenko Fedor 06.02.2020 17:25

Цитата:

Сообщение от drwhite
Может быть это и изыск, но, согласитесь, удобнее)

никакого здезь удобства нет, а говорит о неправильном подходе реализации, что в результатке выташили Крокфорда за уши :)

drwhite 06.02.2020 18:09

Цитата:

Сообщение от Poznakomlus
никакого здезь удобства нет, а говорит о неправильном подходе реализации

Разве не удобней обращаться объекту к самому себе через ссылку, которая всегда сработает, а не через придуманное имя?

Зачем тогда придумали слово constructor вместо имени класса?

И какой подход к реализации был бы правильным?

Vlasenko Fedor 06.02.2020 18:34

<script>
function factorial(x)
{
    var result = 1;
    while (x > 1) result *= x--;
    return result;
}
alert(factorial(5));
</script>

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

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

drwhite 06.02.2020 19:36

Цитата:

Сообщение от Poznakomlus
конструктор служит для иницииализации

Ну хорошо, а если бы вместо this надо было бы использовать имя класса?)

Цитата:

Сообщение от Poznakomlus
честно, полноту вашей задачи я до сих пор не понял

Бывает иногда нужно передать из функции ее имя в другое место, без генерации исключения и раскрутки стека. В пхп есть __FUNCTION__, что очень удобно. Не найдя такого в JS, пришлось зайти на форум и спросить, а вдруг кто-то подскажет.

Цитата:

Сообщение от Poznakomlus
"И какой подход к реализации был бы правильным?"
тот, который понятен другим разработчикам. Чтобы они не ломали голову в раскручивани вызовов ваши функций

Неужто возможность получить имя функции внутри функции кому-то так уж сломает голову?

Скорей Y-комбинатор и замыкания вынесут моск))

PS И потом, как сделать, чтобы код был понятен другим разработчикам, прочитать много умных книг, сходить на разные «курсы», которых сейчас расплодилось как собак нерезаных? Это, положим, можно, есть ли там стандарты и правила написания правильного кода, как узнать?

SuperZen 06.02.2020 20:11

Цитата:

Сообщение от drwhite (Сообщение 519721)
стандарты и правила написания правильного кода, как узнать?

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

Vlasenko Fedor 06.02.2020 21:58

class Test1 {}
class Test2 {
    get name(){ return this.constructor.name}
}
const Test3 = function () {}

const Test4 = function () {
    this.name =()=> this.constructor.name
}
const a = new Test1();
const b = new Test2();
const c = new Test3();
const d = new Test4();
console.log(a.constructor.name);
console.log(b.constructor.name);
console.log(c.constructor.name);
console.log(d.constructor.name);
console.log(b.name);
console.log(d.name());
console.log(Test1.name);

не знаю, может это поможет

Aetae 06.02.2020 22:49

Malleys, просто пишет чушь ради чуши.

Например хипстерское дерьмо, типа:
const factorial = x => {
	if (x === 1)
		return 1;
	else
		return x * factorial(x - 1);
}

alert(factorial(5));
после чего решает проблему которой нет.

Нормальный человек напишет:
function factorial(x) {
	if (x === 1)
		return 1;
	else
		return x * factorial(x - 1);
}
и это будет работать всегда.

Даже если мы потом сделаем так:
function factorial(x) {
	if (x === 1)
		return 1;
	else
		return x * factorial(x - 1);
}

const f = factorial;
alert(f(5));
или так:
// factorial-module.js
function factorial(x) {
	if (x === 1)
		return 1;
	else
		return x * factorial(x - 1);
}

export default factorial;

// main.js
import f from './factorial-module';
alert(f(5));


Если мы хотим пулять функцию по разным переменным сразу после создания, но при этом не хотим засорять область видимости её оригинальным названием(почему?), то всегда можно сделать так:
const factorial = function fcallee(x) {
	if (x === 1)
		return 1;
	else
		return x * fcallee(x - 1);
}


const f = factorial;
const f2 = factorial;

alert(typeof fcallee);
alert(factorial(5));
alert(f(5));
alert(f2(5));


Нет никакой проблемы и никакого смысла в arguments.callee, и уж точно нет смысла в онанизме от Malleys.

Malleys 07.02.2020 00:08

Цитата:

Сообщение от drwhite
Благодарю, все верно, но так хотелось сделать это в одну строчку кода)

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

Цитата:

Сообщение от Poznakomlus
никакого здесь удобства нет, а говорит о неправильном подходе реализации

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

Цитата:

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

Цитата:

Сообщение от drwhite
Это, положим, можно, есть ли там стандарты и правила написания правильного кода, как узнать?

Это зависит от модели программирования. Например, Poznakomlus ошибочно предполагает, что существует единственно истинный способ записи программы, однако используя разные парадигмы для решения одной и той же задачи, можно прийти к выводу, что императивный код не всегда простой, понятный и краткий. Для императивного кода также свойственна ссылочная непрозрачность.

Цитата:

Сообщение от Poznakomlus
избавится от рекурсии

А почему тогда не нужно избавиться от this, constructor, window? Ваши призывы походят на «нравится, а почему, понять не могу».

Цитата:

Сообщение от Aetae
Malleys, просто пишет чушь ради чуши.

Покажите, пожалуйста, как сделать рекурсию при помощи стрелочной функции без зависимости от имени переменной.

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

Обход всех текстовых узлов в DOM
var Y=f=>(g=>x=>f(g(g))(x))(g=>x=>f(g(g))(x));

var processTextNodes = visit => Y(order => node => {
	if(node != null) node.childNodes.forEach(childNode =>
		childNode.nodeType === Node.TEXT_NODE ? visit(childNode) : order(childNode)
	)
});

var logTextNotes = processTextNodes(console.log);
logTextNotes(document.body);
Poznakomlus, попробуйте избавиться от рекурсии и вы закончите кодом, про который не скажешь, что он, по сравнению с этим — простой, понятный и краткий!

Aetae, то, что вы описали, работает с обычными функциями (и это хорошо!), а не со стрелочными.

Зависит от имени...
const factorial = x => x === 1 ? 1 : x * factorial(x - 1);
Y-комбинатор позволяет убрать такую зависимость...
import { Y } from "combinators-js";
const factorial = Y(f => x => x === 1 ? 1 : x * f(x - 1));

Vlasenko Fedor 07.02.2020 00:50

Malleys,
хватит тролить уже :lol:
function logTextNotes(el) {
    var n, walk = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null, false);
    while (n = walk.nextNode()) console.log(n);
}
logTextNotes(document.body);

рони 07.02.2020 00:58

Цитата:

Сообщение от Malleys
Обход всех текстовых узлов в DOM

:-?
<!DOCTYPE html>
<html>
<head>
  <title>Untitled</title>
  <meta charset="utf-8">


</head>
<body>
<div>12345</div>
<script>
var Y=f=>(g=>x=>f(g(g))(x))(g=>x=>f(g(g))(x));

var processTextNodes = visit => Y(order => node => {
	if(node != null) node.childNodes.forEach(childNode =>
		childNode.nodeType === Node.TEXT_NODE ? visit(childNode) : order(childNode)
	)
});

var logTextNotes = processTextNodes(console.log);
logTextNotes(document.body);

/* https://developer.mozilla.org/ru/docs/Web/API/document/createTreeWalker */
function textNodesLog(el){
  const tree = document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false);
  while(tree.nextNode()) console.log(tree.currentNode);
}
textNodesLog(document.body)
  </script>
</body>
</html>

рони 07.02.2020 01:00

Poznakomlus,
:)

Aetae 07.02.2020 01:15

ИМХО, Malleys надо бы забанить. Говорить с ним точно нет никакого смысла.
Он либо дурачок, либо тролль, либо всё вместе взятое(Maxmaxmaximus, как твой фреймворк?).

P.S. Если кому интересно откуда взялся этот его бессмысленный Y-комбинатор - в рамках js он, как и прочие комбинаторы, просто математическая зарядка для ума, самоочевидно не предназначенная ни для какого практического применения.

Malleys 07.02.2020 01:41

Poznakomlus, рони, да у вас правильное решение, но это не равнозначная замена приведённому коду (в принципе представьте обход любого дерева)
function processTextNodes(node, visit) {
	if(node == null) return;

	const stack = [];
	stack.push(node);

	while(stack.length !== 0) {
		node = stack.pop();

		if(node.nodeType === Node.TEXT_NODE)
			visit(node);

		for(const childNode of node.childNodes) {
			stack.push(childNode);
		}
	}
}

processTextNodes(document, console.log);


Цитата:

Сообщение от Aetae
ИМХО, Malleys надо бы забанить

Всё это походит на сжигание людей в средние века. На тюремные заключения в современном РФ. Что именно я сделал не так?

Цитата:

Сообщение от Aetae
Говорить с ним точно нет никакого смысла.

Я вас ни к чему не принуждаю, я прочитал вашу точку зрения насчёт не стрелочных функции, считаю ОК. А вы разве что-то у меня спрашивали?

Цитата:

Сообщение от Aetae
Он либо дурачок, либо тролль, либо всё вместе взятое

Проблемы?

Malleys 07.02.2020 01:57

Цитата:

Сообщение от Aetae
Если кому интересно откуда взялся этот его бессмысленный Y-комбинатор - в рамках js он, как и прочие комбинаторы, просто математическая зарядка для ума, самоочевидно не предназначенная ни для какого практического применения.

Хватит вводить людей в заблуждение! Оно прекрасно работает как замена классической рекурсии, о которой вы упоминали.

Вы что-то не так поняли, я как бы не против классической рекурсии.

В этой теме я упомянул Y-комбинатор, как одно из возможных решении, поскольку автор темы не указал конкретный код с которым он работает (Может на самом деле можно обойтись без рекурсии вообще!)

drwhite 07.02.2020 09:02

Цитата:

Сообщение от Malleys
Обход всех текстовых узлов в DOM
var Y=f=>(g=>x=>f(g(g))(x))(g=>x=>f(g(g))(x));
var processTextNodes = visit => Y(order => node => {
    if(node != null) node.childNodes.forEach(childNode =>
        childNode.nodeType === Node.TEXT_NODE ? visit(childNode) : order(childNode)
    )
});
var logTextNotes = processTextNodes(console.log);
logTextNotes(document.body);

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

За такую конструкцию в средние века точно бы на костер поставили =)

Цитата:

Сообщение от Poznakomlus
Код должен быть простым и понятым

Цитата:

Сообщение от Aetae
P.S. Если кому интересно откуда взялся этот его бессмысленный Y-комбинатор - в рамках js он, как и прочие комбинаторы, просто математическая зарядка для ума, самоочевидно не предназначенная ни для какого практического применения.

Вот здесь немного есть
И здесь

Alexandroppolus 07.02.2020 09:29

Y-комбинатор, конечно, прикольный, на собеседовании вполне можно закарать соискателя )

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

----
если у кого "мозги чешутся" повоевать с лямбдами, то велкам:
https://www.codewars.com/kata/57c4a3...65b/javascript
я пока не решил, там всё не так просто как кажется на первый взгляд )
ничего парсить не надо, на вход уже готовое дерево, только вычислить

ruslan_mart 07.02.2020 13:15

Что-то пробежался по треду одним глазом, но так и не понял зачем это нужно.

Для рекурсии ничего не мешает сделать так:

const factorial = function f() {
    console.log(typeof f); // "function";
}

const factorial2 = factorial;

console.log(typeof factorial); // "function";
console.log(typeof factorial2); // "function";
console.log(typeof f); // "undefined";

factorial();
factorial2();



А для класса можно просто внутри использовать:

this.constructor



Например:

// ...
  cloneObject() {
     return new this.consructor(this.data);
  }

 // ...



Если Вам действительно нужно как-то получить имя функции, то это просто неправильный подход.
И не нужно смотреть на PHP. Все эти магические константы на PHP нужны для отладки, не более.

drwhite 12.02.2020 22:48

Как правильно реализовать такую необходимость без Node:

class Account
{
    constructor(){...}
    async DB_createAccount()
    {
        let response = await fetch('lib/actionsAJAX.php?action='+{имя функции}+'&login='this.login);
        if (response.ok) 
        {
            .....
        }
        else
        {
	
        }
    }
}


Часовой пояс GMT +3, время: 23:12.