Рекурсия. Почему?
На днях столкнулся с таким вот противоречием. До сих пор так и не выяснил в чем же проблема. Есть рекурсивная функция, которая выводит полный путь файла из базы данны. То есть все уже давно проиндексировано и занесено в базу со связями файл_id-папка_id. Вот первоначальный код с выводом результата:
function GetFileRootById($Id, $root = null){
$res = $this->db->SelectRow(’SELECT dir_name, dir_related_dir ‘ .
‘FROM dir ‘ .
‘WHERE dir_id = ?’,
$Id);
$root = $res[’dir_name’].’/’.$root;
if ($res[’dir_related_dir’] == 0){
return $root;
}
else{
$this->GetFileRootById($res[’dir_related_dir’], $root);
}
}
$result = GetFileRootById(123);
var_dump($result); // NULL
Вопрос: почему она постоянно возвращает NULL, тогда как написанное ниже возвращает значение? Отличие в том, что результат я передаю по ссылке:
function GetFileRootById($Id, &$root = null){
$res = $this->db->SelectRow(’SELECT dir_name, dir_related_dir ‘ .
‘FROM dir ‘ .
‘WHERE dir_id = ?’,
$Id);
$root = $res[’dir_name’].’/’.$root;
if ($res[’dir_related_dir’] == 0){
return;
}
else{
$this->GetFileRootById($res[’dir_related_dir’], $root);
}
}
$result = ”;
GetFileRootById(123, $result);
var_dump($result); // folder1/folder2/etc
P.S. Надо в редакторе заметок сделать нормальные вставки code, а то так он плохо читается.

Потому что надо внимательно смотреть, что ты пишешь.
Первый вариант не работает, потому что когда функция входит в рекурскию для поиска вышестоящего узла (dir_related_dir > 0; кстати, почему такое гнусное неочевидное название для очевидной вещи?), то результат не возвращается:
else
{
$this->GetFileRootById($res[’dir_related_dir’], $root);
}
не понял, что значит “гнусное неочевидное название”?)
кстати, если сделать
if ($res[’dir_related_dir’] == 0){
echo $root;
return $root;
}
то выведет путь.
Суть в том, что не срабатывает return $root;
По пунктам
1. Гнусное неочевидное — потому что dir_related_dir ни о чём не говорит. Parent_id скажет о поле в тысячу раз больше информации.
2. Естественно выведет. Но это же рекурсивная функция которая _возвращает_ результат (это очень важный момент!), а значит что если ты вызываешь её внутри себя, то ты должен результат внутреннего вызова либо присваивать, либо делать return. Я тебе написал, где у тебя ошибка (нет return при вызове):
else{
$this->GetFileRootById($res[’dir_related_dir’], $root);
}
Если добавить тут return, то когда вызовы доберутся до корня дерева, то произойдёт возврат склеенного пути из самого “низа” рекурсивного стека наверх.
Мне просто кажется, что ты тут не совсем понимаешь суть рекурсии. Если у тебя два узла (представь), то на первом узле ты вызовешь себя, где будет возвращен склеенный путь. Но ты ведь внутренний вызов никак не обрабатываешь, не присваиваешь и не возвращаешь.
Хм.. может ты неправильно понял.. дело в том, что этот return вызывается в последний раз, и только один раз. echo в предыдущем комменте вызывается 1 раз.
В конце рекурсии он сюда
else{
$this->GetFileRootById($res[’dir_related_dir’], $root);
}
не зайдет.
Такого не бывает, чувак.
Ты где-то что-то упускаешь.
И я тебе скажу, что.
У тебя ф-ция два раза вызывается. Первый раз вызываешь её ты, где происходит повторный рекурсивный вход, где и выводится $root. Но т.к. вызов рекурсивный, то результат не возвращается.
Ещё раз говорю, добавь return. Это баг который сидит с транспаратном и орёт что он баг.
>>>Ещё раз говорю, добавь return. Это баг который сидит с транспаратном и орёт что он баг
Куда добавить return?
Ты издеваешься?
В результат рекурсивного вызова, я же два раза написал.
У тебя:
else{
$this->GetFileRootById($res[’dir_related_dir’], $root);
}
А надо:
else{
return $this->GetFileRootById($res[’dir_related_dir’], $root);
}
Phoebus, ты абсолютно прав. Так и есть. :) Спасибо.
>>>а значит что если ты вызываешь её внутри себя, то ты должен результат внутреннего вызова либо присваивать, либо делать return
Этого я совсем не знал. Нелогично как-то:)
Это же очевидно!
Нарисуй на листе бумаги обычным карандашом стек, или представь это абстракцией, тут не надо знать “что результат рекурсии надо возвращать”, тут достаточно понимать, _что_ именно происходит когда ты рекурсивно вызываешь.
>>>а значит что если ты вызываешь её внутри себя, то ты должен результат внутреннего вызова либо присваивать, либо делать return
>Этого я совсем не знал. Нелогично как-то:)
Забыл добавить “в данном случае”. Во втором случае ты хранишь всё в одной-единственной переменной, которую тягаешь по ссылки во все вызовы. Там соответсвенно никакой результат возвращать не нужно, потому что результат хранится и изменяется в одной для всех вызовов переменной.
геп, ты спал перед тем как анализировать код и писать комменты? =)
хватит тыкать меня в то, что спал я или нет) Тем более разговор был в феврале, я уже не вспомню. :) Может невнимательность по другим причинам.
ой, она у меня сегодня в рсске как непрочтенная появилась, виноват. я еще удивился, вроде вечером не было, а феба видел час назад, как вы успали столько комментов настрочить =)
Вообще говоря, я дамп восстанавливал, а эта запись была добавлена, изменена дата. В комментах вручную были изменены id поста.