Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Косвенная рекурсия в Javascript (https://javascript.ru/forum/misc/30029-kosvennaya-rekursiya-v-javascript.html)

hrundel 21.07.2012 18:09

Косвенная рекурсия в Javascript
 
Пытаюсь разобраться в рекурсии. Почему когда я пишу так:
<script type="text/javascript">
function recursMe(param) {
     if (param < 0) {   //base case
          return -1;
     }
     else {

          //some code here
          recursMe(param);
     }
}
recursMe(10);

	</script>

или так (используя косвенную рекурсию):
<script type="text/javascript">
function recursMe(param) {
     if (param < 0) {   //base case
          return -1;
     }
     else {

          //some code here
          //recursMe(param);
		recursMe2(param);
     }
}
function recursMe2(param)
{
	 recursMe(param);
}
recursMe(10);

	</script>

во всех браузерах я имею ошибку примерно такого характера:
Хром: Uncaught RangeError: Maximum call stack size exceeded
ie: Stack overflow at line:135


Когда я делаю косвенную рекурсию через setTimeout() так:
<script type="text/javascript">
function recursMe(param) {
     if (param < 0) {   //base case
          return -1;
     }
     else {

          //some code here
          setTimeout("recursMe(" + param + ")", 1);
     }
}
recursMe(10);

	</script>

браузеры не выдают ошибку, хотя происходит та же ситуация: функция recursMe() вызывается много раз подряд. Почему так происходит?

nerv_ 21.07.2012 18:28

hrundel, http://css-live.ru/javascript/javasc...rekursiya.html - не самая хорошая статья.

Что проиходит в первом коде, приведенном Вами? Подсказка:
Цитата:

Сообщение от hrundel
Хром: Uncaught RangeError: Maximum call stack size exceeded


Лично я бы не стал разбирать рекурсию на таких отвлеченных примерах. Для начала попробуйте зарыться в DOM.

vadim5june 21.07.2012 18:43

Цитата:

Сообщение от hrundel (Сообщение 190537)
браузеры не выдают ошибку, хотя происходит та же ситуация: функция recursMe() вызывается много раз подряд. Почему так происходит?

стек не переполняется-потому что SEtTimeout выполняется в глобальном контексте
там же ошибка переполнения стека
Stack overflow at line:135
с setTimiout у Вас нерекурсивное обращение к функции

hrundel 22.07.2012 17:59

Цитата:

Сообщение от vadim5june
стек не переполняется-потому что SEtTimeout выполняется в глобальном контексте
там же ошибка переполнения стека
Stack overflow at line:135
с setTimiout у Вас нерекурсивное обращение к функции

А как обычному человеку определять, когда рекурсивно обращение к функции, а когда нерекурсивное? Я считаю, что косвенная рекурсия всегда вызвана одним и тем же - функция вызывает саму себя через цепочку других функций. Здесь такая ситуация и во втором примере и в третьем, но результат разный.
Что за глобальный контекст у setTimeout()? Как он может влиять на переполнение стека?

B@rmaley.e><e 22.07.2012 18:06

Цитата:

Сообщение от hrundel
Здесь такая ситуация и во втором примере и в третьем

Наглая ложь. Рекурсия имеет место, когда внутри функции нужно получить результат её вызова от других (впрочем, не обязательно отличных от текущих) аргументов. В случае с setTimeout никаких результатов никто дожидаться не будет. JS где-то у себя внутри сделает пометку "через сколько-то мс запустить такую-то функцию" и спокойно продолжит исполнение.

vadim5june 22.07.2012 19:13

Цитата:

Сообщение от hrundel (Сообщение 190728)
Я считаю, что косвенная рекурсия всегда вызвана одним и тем же - функция вызывает саму себя через цепочку других функций. Здесь такая ситуация и во втором примере и в третьем, но результат разный.
Что за глобальный контекст у setTimeout()? Как он может влиять на переполнение стека?

когда вызывается setTimeout эта цепочка прерывается
В чем смысл рекурсии-когда функция вызывает саму себя то текущее состояние записывается в стек -потому что потом должны быть выполнены команды которые стоят после операции вызова
Это обычно например обход дерева DOM
А когда выполняется команда setTimeout записи в стек не будет потому что seTimeout асинхронная команда
Цитата:

Сообщение от hrundel (Сообщение 190728)
Что за глобальный контекст у setTimeout()? Как он может влиять на переполнение стека?

Вы не знаете разве что функции записанные в setTimeout выполняются в глобальном контексте window а не в контексте объекта вызывающей функции

oneguy 24.07.2012 05:27

Цитата:

Сообщение от vadim5june
Вы не знаете разве что функции записанные в setTimeout выполняются в глобальном контексте window а не в контексте объекта вызывающей функции

А вот и неправда :) Функции, записанные в setTimeout выполняются в том контексте, в которым они определены. Пример:
var f=function () {
  var b=1;
  return function () {
    alert(b);
  };
}();
setTimeout(f, 0);

vadim5june 24.07.2012 07:56

Цитата:

Сообщение от oneguy (Сообщение 191141)
А вот и неправда :) Функции, записанные в setTimeout выполняются в том контексте, в которым они определены.
[/JS]

я то имел ввиду вызывающую функцию-это функция где сам setTimout вызывается
у Вас в примере setTimout вызывается в глобальном контексте

Функция выполняется в другом контексте, отличном от контекста, в котором задается setTimeout.
При этом значение this = window, поэтому о передаче правильного this надо позаботиться отдельно.

http://javascript.ru/setTimeout
правда контекст к рекурсии никакого отношения не имеет-а имеет значение асинхронность setTimout
Этот метод выполняет код(или функцию), указанный в первом аргументе, асинхронно, с задержкой в delay миллисекунд.
я так понимаю что даже если указать delay=0 все равно метод будет выполнятся асинхронно

hrundel 24.07.2012 16:58

Цитата:

Сообщение от oneguy (Сообщение 191141)
А вот и неправда :) Функции, записанные в setTimeout выполняются в том контексте, в которым они определены. Пример:
var f=function () {
  var b=1;
  return function () {
    alert(b);
  };
}();
setTimeout(f, 0);

Странный пример :) Непонятно, как он что-то может доказать? По-моему, очевидный результат, равный '1'. Что-то ваша мысль до конца не раскрыта.

oneguy 24.07.2012 23:16

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

devote 25.07.2012 07:42

Цитата:

Сообщение от Дзен-трансгуманист
Тогда уж так:

и в чем тут разница? что-то понять не могу

bes 25.07.2012 16:08

Цитата:

Сообщение от devote
и в чем тут разница? что-то понять не могу

очередная особенность FF

oneguy 25.07.2012 16:14

Цитата:

Сообщение от bes
очередная особенность FF

Спецификация ECMAScript запрещает использование объявлений функций внутри блоков, в том числе switch, if, try и др. Все объявления функции должны находиться непосредственно в коде верхнего уровня, коде функции или коде eval.
Поэтому оба кода, приведенные выше, являются невалидными, хотя браузер не выдаёт ошибку.

bes 25.07.2012 16:26

Цитата:

Сообщение от oneguy
Поэтому оба кода, приведенные выше, являются невалидными, хотя браузер не выдаёт ошибку.

И более того - исполняет (кроме FF в первом примере)

devote 25.07.2012 16:30

oneguy,
ты какую там спецификацию то смотришь родной? а то прям любишь тыкать на нее, да вот аргументов нет.. Читай внимательно спецификацию, что бы дураком перед другими не казаться:
"use strict";
try {
  var i = 0;
  function a () { i++; a(); }
  a();
}
catch (e) {
  alert('ваш ёбаный стек равен каким-то сраным: '+i+' вызовам');
}
спецификация запрещает это делать в строгом режиме

oneguy 25.07.2012 16:39

Да, сейчас я аргументирую своё утверждение с помощью спецификации.
Спецификация запрещает объявлять функции внутри блоков в любом режиме, не только строгом.
http://es5.javascript.ru/x12.html#x12 - здесь приведен список возможностей для Statement и в нём нет FunctionDeclaration.
Там же приводится замечание, касающееся нашей темы.
Цитата:

ПРИМЕЧАНИЕ Известно, что несколько широко распространённых реализаций языка ECMAScript поддерживают использование FunctionDeclaration Объявление функции в качестве инструкции. Однако в семантике, применимой к таким объявлениям функций, имеются существенные противоречивые вариации реализаций. Из-за этих противоречий код, написанный с использованием FunctionDeclaration в качестве Statement, не является надёжным при переносе из одной реализации в другую. Поэтому для реализаций ECMAScript рекомендуется либо отключать использование FunctionDeclaration, либо генерировать предупреждение в случае такого использования. Возможно, в последующих версиях языка ECMAScript появятся альтернативные средства для переноса кода, позволяющие объявлять функции в контексте Statement.

hrundel 25.07.2012 16:40

Цитата:

Сообщение от Дзен-трансгуманист (Сообщение 191476)
Maxmaxmахimus,
Тогда уж так:
try {
  var i = 0;
  function a () { i++; a(); }
  a();
}
catch (e) {
  alert('ваш ёбаный стек равен каким-то сраным: '+i+' вызовам');
}
А то в твоем варианте получается 0. :haha:

Ваш код загрузил мой Firefox 11 так, что он завис.
Код:

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

Сценарий: resource://firebug/firebug-service.js:3253


bes 25.07.2012 16:42

devote, я считаю, что у oneguy правильный подход, куда ещё тыкаться как не в спецификацию, только надо ссылку кидать, где в спецификации эта информация (спецификация-то большая).

devote 25.07.2012 16:44

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

devote 25.07.2012 16:44

Цитата:

Сообщение от bes
только надо ссылку кидать, где в спецификации эта информация (спецификация-то большая).

ссылку надо кидать на оригинал

bes 25.07.2012 16:54

Цитата:

Сообщение от devote
ссылку надо кидать на оригинал

Цитата:

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

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

oneguy 25.07.2012 16:54

Вот оригинал спецификации (на английском языке, в PDF формате)
http://www.ecma-international.org/pu...T/Ecma-262.pdf
Смотрите 12 раздел.

devote 25.07.2012 17:07

oneguy,
тяжкая документация, но не слово об операторах в скобках. Видимо где то в других разделах описано, а в этом описано лишь это:
v=1;
var foo = {bar: function () {alert(this.v);}, v:2};
 
false || foo.bar(); // 2 (у меня 1) // тоесть описана работа без скобок

oneguy 25.07.2012 17:13

Цитата:

Сообщение от devote
oneguy,
тяжкая документация, но не слово об операторах в скобках. Видимо где то в других разделах описано, а в этом описано лишь это:

Вы, наверно, написали не в ту тему, поэтому отвечу в нужную тему.


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