Получить имя функции внутри функции не используя callee
Приветствую, камрады, можно ли сделать как в PHP:
public function func($in_params) { $__F__ = __FUNCTION__; ... if (...) $__F__($in_params); } чтобы не зависеть от имени функции, но не используя callee? |
Пример варианта для 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> |
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Обращение функции к самой себе вообще не предполагает наличие глобальной ссылки. Т. е. поскольку она не называет себя по имени, она не связана тесно с самой собой. |
:write: может я что-то не понимаю, но альтернативы arguments.callee в решениях выше Poznakomlus и
Malleys не увидел. |
Потому что её нет.
Если вы работаете в нестрогом режиме - у вас уже есть arguments.callee, никакого смысла в чём-то ещё нет. Если вы работаете в строгом режиме - arguments.callee у вас отобрали специально, чтобы заметно ускорить и облегчить исполнение избавившись от сложных взаимосвязей и побочек. Альтернативы, соответственно, тут принципиально не будет. |
Цитата:
Цитата:
— нет, уж лучше вы к нам» =) Цитата:
|
Цитата:
Цитата:
Цитата:
|
Цитата:
|
Цитата:
function func($params) { ... if (...) self($params); } чтобы имя функции указывалось один раз, без лишних переменных, без NFE и в strict mode =) |
<script> function test() { try { throw new Error( "Get name") } catch (e) { console.log(e.stack); } } test(); </script> название функции есть в стеке вызовов, однако этот стек по разному работает в броузерах если я вас правильно понял |
Цитата:
Цитата:
А значит вам нужно самим указать аргумент и это правильно... function func(self, $params) { ... if (...) self($params); }И это использует имя функции только один раз, нет лишних переменных, есть strict mode и чистота функции. Вопрос только в том, как её запустить! |
Цитата:
Это даже покруче, чем eval) |
Цитата:
|
Цитата:
Цитата:
Рассмотрим рекурсивную функцию... 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, это именно то, что вы хотели! |
Функция 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 |
Цитата:
|
Цитата:
|
Malleys,
возможно и я когда нибудь пойму, что вы здесь написали ... :) :( :( :( |
Цитата:
здесь надо просто взять вызов Y(...) и размотать в обратную сторону, тогда будет понятно, что и как вызывается |
Цитата:
Цитата:
/* библиотека комбинаторов */ 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])) |
Цитата:
|
Цитата:
Цитата:
Может быть это и изыск, но, согласитесь, удобнее) |
Цитата:
|
Цитата:
Зачем тогда придумали слово constructor вместо имени класса? И какой подход к реализации был бы правильным? |
<script> function factorial(x) { var result = 1; while (x > 1) result *= x--; return result; } alert(factorial(5)); </script> пример факториала честно, полноту вашей задачи я до сих пор не понял хотите избавится от рекурсии используйте циклы конструктор служит для иницииализации "И какой подход к реализации был бы правильным?" тот, который понятен другим разработчикам. Чтобы они не ломали голову в раскручивани вызовов ваши функций. Код должен быть простым и понятым |
Цитата:
Цитата:
Цитата:
Скорей Y-комбинатор и замыкания вынесут моск)) PS И потом, как сделать, чтобы код был понятен другим разработчикам, прочитать много умных книг, сходить на разные «курсы», которых сейчас расплодилось как собак нерезаных? Это, положим, можно, есть ли там стандарты и правила написания правильного кода, как узнать? |
Цитата:
|
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); не знаю, может это поможет |
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. |
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Вот, например, более практическая задача, чем вычисление факториалов, на примере которой демонстрируется рекурсия... Обход всех текстовых узлов в 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)); |
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); |
Цитата:
<!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> |
Poznakomlus,
:) |
ИМХО, Malleys надо бы забанить. Говорить с ним точно нет никакого смысла.
Он либо дурачок, либо тролль, либо всё вместе взятое(Maxmaxmaximus, как твой фреймворк?). P.S. Если кому интересно откуда взялся этот его бессмысленный Y-комбинатор - в рамках js он, как и прочие комбинаторы, просто математическая зарядка для ума, самоочевидно не предназначенная ни для какого практического применения. |
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); Цитата:
Цитата:
Цитата:
|
Цитата:
Вы что-то не так поняли, я как бы не против классической рекурсии. В этой теме я упомянул Y-комбинатор, как одно из возможных решении, поскольку автор темы не указал конкретный код с которым он работает (Может на самом деле можно обойтись без рекурсии вообще!) |
Цитата:
var Y=f=>(g=>x=>f(g(g))(x))(g=>x=>f(g(g))(x)); За такую конструкцию в средние века точно бы на костер поставили =) Цитата:
Цитата:
И здесь |
Y-комбинатор, конечно, прикольный, на собеседовании вполне можно закарать соискателя )
но он втыкает несколько дополнительных вызовов на каждой итерации, что и на скорости может сказаться, и дебажить веселее (нельзя просто взять и зайти внутрь рекурсивного вызова одним нажатием на F11). В общем, вернувшись в реальность, лично я не стал бы его на практике. ---- если у кого "мозги чешутся" повоевать с лямбдами, то велкам: https://www.codewars.com/kata/57c4a3...65b/javascript я пока не решил, там всё не так просто как кажется на первый взгляд ) ничего парсить не надо, на вход уже готовое дерево, только вычислить |
Что-то пробежался по треду одним глазом, но так и не понял зачем это нужно.
Для рекурсии ничего не мешает сделать так: 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 нужны для отладки, не более. |
Как правильно реализовать такую необходимость без 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. |