Где Perl хранит промежуточные результаты в рекурсивной реализации вычисления факториала N?

Существует рекурсивная реализация вычисления факториала N на Perl.

sub fact {
  my ($n) = shift;
  return $n if $n <= 2;
  return $n * fact($n - 1);
}

Может ли кто-нибудь объяснить мне, где Perl хранит промежуточные результаты до того, как функция даст результат?

UPD и как я могу их увидеть с помощью отладчика или с помощью чего-то еще?

Из ответов мне объяснили, что эти значения хранятся в стеке, но как я могу увидеть эти значения из стека?


person edem    schedule 01.01.2013    source источник
comment
Например, результат рекурсивного вызова fact, или $n, или...? В стеке.   -  person Ry-♦    schedule 01.01.2013
comment
@minitech вы знаете, как получить доступ к этим промежуточным результатам из стека?   -  person edem    schedule 01.01.2013
comment
Стек не является явным в Perl. Вы получаете доступ к $n, просто используя эту переменную. Вызов функции просто возвращает возвращаемое значение. Perl творит чудеса за вас. Если вы пытаетесь решить конкретную проблему, может помочь сообщение что вы пытаетесь сделать, а не как. (Если это академическая проблема, я все еще не совсем понимаю)   -  person amon    schedule 01.01.2013
comment
@edem, вы получаете доступ к этим промежуточным значениям с помощью оператора умножения. Вы также можете получить к ним доступ, используя оператор присваивания. my $f = fact($n - 1);   -  person ikegami    schedule 02.01.2013
comment
@ikegami Да, вы правы, но я хочу понять, как это работает? Например, в цикле я могу вывести результат каждой переменной, используемой в проге с помощью отладчика. Итак, как я могу увидеть, какие значения хранятся в частях выражения с помощью отладчика или чего-то еще?   -  person edem    schedule 04.01.2013
comment
@edem, тогда он тоже использует стек. Каждый из аргументов находится в стеке, и print получает их оттуда. Упрощенно: print($a, $b, $c*$d) равно push @stack, $a; push @stack, $b; push @stack, $c; push @stack, $d; multiply; print;   -  person ikegami    schedule 04.01.2013
comment
Нет ответа, который покажет, как пользователь может следить за этим процессом. Не просто распечатать, а увидеть его из отладчика perl или с помощью чего-то еще.   -  person edem    schedule 09.01.2013


Ответы (4)


Скаляр, возвращаемый $n, сохраняется в стеке.

Вот как выглядит стек непосредственно перед вызовом fact:

  • Скаляр, возвращаемый $n на уровне рекурсии 0
  • List:
    • Scalar returned by $n in recursion level 1
    • List:
      • Scalar returned by $n in recursion level 2
      • List:
        • Scalar returned by $n-1 in recursion level 2
        • Скаляр, возвращаемый \&fact на уровне рекурсии 2

Вот как выглядит стек сразу после вызова fact:

  • Скаляр, возвращаемый $n на уровне рекурсии 0
  • List:
    • Scalar returned by $n in recursion level 1
    • List:
      • Scalar returned by $n in recursion level 2
      • Скаляр, возвращаемый fact($n - 1) на уровне рекурсии 2

В этот момент оператор умножения умножит два последних значения в стеке и поместит результат в стек.

  • Скаляр, возвращаемый $n на уровне рекурсии 0
  • List:
    • Scalar returned by $n in recursion level 1
    • List:
      • Scalar returned by $n * fact($n - 1) in recursion level 2

Затем саб возвращается,

  • Скаляр, возвращаемый $n на уровне рекурсии 0
  • List:
    • Scalar returned by $n in recursion level 1
    • Скаляр, возвращаемый fact($n - 1) на уровне рекурсии 1

И так далее.

person ikegami    schedule 01.01.2013
comment
Я понимаю, как это работает и что будет возвращено с каждого уровня рекурсии, но я не знаю, как это выглядит в стеке. - person edem; 04.01.2013
comment
Каждая пуля является элементом в стеке. Это своего рода массив. - person ikegami; 04.01.2013

При рекурсии любой из аргументов, которые передаются в каждом вызове, попадают в кадр/стек вызова. Используя Carp & Cluck, вы можете видеть кадры вызова. Промежуточные результаты рассчитываются, пока стек раскручивается по достижении базового случая ($v == 1). Может ли это быть только в регистре процессора? И оператор (*) умножает этот промежуточный результат на $v, который находится в стеке. Также ознакомьтесь с этой статьей.

#!/usr/bin/env  perl

use strict;
use IO::Handle;
use Carp qw(cluck);

STDOUT->autoflush(1);
STDERR->autoflush(1);

sub factorial {
    my $v = shift;

    dummy_func();
    return 1 if $v == 1;
    print "Variable v value: $v and it's address:", \$v, "\ncurrent sub factorial addr:", \&factorial, "\n","-"x40;
    return $v * factorial($v - 1);
}

sub dummy_func {
    cluck;
}

factorial(5);

Также поможет запуск в режиме отладки.

perl -d factorial.pl

person Shiva    schedule 21.02.2013
comment
Спасибо, что опубликовали свой ответ! Обязательно внимательно прочитайте Часто задаваемые вопросы о саморекламе. Также обратите внимание, что требуется публиковать заявление об отказе от ответственности каждый раз, когда вы ссылаетесь на свой собственный сайт/продукт. - person Andrew Barber; 21.02.2013

Поскольку $n объявлен как my $n, это переменная с лексической областью видимости, которая хранится в стеке, а не в системной таблице. Дополнительную информацию см. в разделе Переменные Perl через my(). Информация.

person David W.    schedule 01.01.2013
comment
1. Лексические переменные не хранятся в стеке. Они хранятся на подушке. Приблизительно это структура, похожая на $pad[$recursion_depth][$var_idx]. 2. Это не отвечает на вопрос ОП, поскольку он не хочет знать, где хранится $n, но где хранится значение, возвращаемое $n. - person ikegami; 01.01.2013

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

return $n * fact($n - 1);

обрабатывается эквивалентно:

my $temp = fact($n - 1);
return $n * $temp;

ОБНОВЛЕНИЕ: я вижу, вас также интересует, где хранится товар перед его возвратом. Это также временно в стеке, поэтому это эквивалентно:

my $temp1 = fact($n - 1);
my $temp2 = $n * $temp1;
return $temp2;
person Barmar    schedule 01.01.2013
comment
В этом примере мы можем получить доступ к переменной $temp, просто добавив строку с print $temp, но в моем примере я не могу этого сделать - person edem; 01.01.2013
comment
Почему вы не можете добавить $temp к вашему примеру? - person Barmar; 01.01.2013
comment
Потому что мы не знаем, какое значение $n будет установлено. Другими словами, мне не нужно просто печатать значение, и я хочу знать, как Perl удерживает их в себе. - person edem; 01.01.2013
comment
Я не знаю, о чем вы говорите, в моем ответе ничего о печати нет. Perl (и любой другой язык, который вы, вероятно, будете использовать) хранит возвращаемое значение вызовов функций в стеке в безымянных временных переменных. Это просто базовая информатика. - person Barmar; 01.01.2013
comment
О, теперь я вижу. Вы не спрашиваете, где находится возвращаемое значение рекурсивного вызова, вы хотите знать, где хранится временное произведение $n и fact($n - 1). Я обновлю свой ответ. - person Barmar; 01.01.2013
comment
Лексические переменные не хранятся в стеке. Они хранятся на подушке. Таким образом, ваши два фрагмента не эквивалентны (даже если вы не изменили порядок оценки). - person ikegami; 01.01.2013