Perl рекурсия и функции - PullRequest
2 голосов
/ 25 июля 2011

Узнав о Perl в течение года, я решил уделить ему несколько часов своего времени, чтобы посмотреть, сколько я смогу поднять.Я хорошо освоил основы, а затем попал в петли.В качестве теста я хотел посмотреть, смогу ли я создать скрипт для рекурсии по всем буквенно-цифровым значениям длиной до 4 символов.Я написал PHP-код, который сделал то же самое некоторое время назад, поэтому я использовал ту же концепцию и использовал ее.Однако, когда я запускаю скрипт, он ставит «а» в качестве первых 3 значений, а затем перебирает только последнюю цифру.Кто-нибудь видит, что я делаю неправильно?

#!/usr/local/bin/perl 

$chars = "abcdefghijklmnopqrstuvwxyz";
$chars .= "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$chars .= "0123456789";

@charset = split(//, $chars);

$charset_length = scalar(@charset);

sub recurse
{
 ($width, $position, $base_string) = @_;

for ($i = 0; $i < $charset_length; ++$i) {
    $base = $base_string . $charset[$i];
    if ($position < $width - 1) {
        $pos = $position + 1;
        recurse($width, $pos, $base);
    }
    print $base;
    print "\n";
}
}

recurse(4, 0, '');

Вот что я получаю при запуске:

aaaa
aaab
aaac
aaad
aaae
aaaf
aaag
aaah
aaai
aaaj
aaak
aaal
aaam
aaan
aaao
aaap
aaaq
aaar
aaas
aaat
aaau
aaav
aaaw
aaax
aaay
aaaz
aaaA
aaaB
aaaC
aaaD
aaaE
aaaF
aaaG
aaaH
aaaI
aaaJ
aaaK
aaaL
aaaM
aaaN
aaaO
aaaP
aaaQ
aaaR
aaaS
aaaT
aaaU
aaaV
aaaW
aaaX
aaaY
aaaZ
aaa0
aaa1
aaa2
aaa3
aaa4
aaa5
aaa6
aaa7
aaa8
aaa9
aaa9
aaa9
aaa9

Ответы [ 5 ]

11 голосов
/ 25 июля 2011

Вы были укушены ненадлежащим ограничением, этот код делает то, что должен (обратите внимание на строгое использование вверху и последующее использование my, чтобы гарантировать переменное определение объема).

10 голосов
/ 25 июля 2011

Уже хорошо ответили, но более идиоматический подход был бы:

use strict;
use warnings;

sub recurse {
    my ($width, $base_string, $charset) = @_;

    if (length $base_string) {
        print "$base_string\n";
    }
    if (length($base_string) < $width) {
        $recurser->($base_string . $_) for @$charset;
    }
}

my @charset = ('a'..'z', 'A'..'Z', '0'..'9');
recurse(4, '', \@charset);

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

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

use strict;
use warnings;

sub make_recurser {
    my ($width, $charset) = @_;
    my $recurser;
    $recurser = sub {
        my ($base_string) = @_;

        if (length $base_string) {
            print "$base_string\n";
        }
        if (length($base_string) < $width) {
            $recurser->($base_string . $_) for @$charset;
        }
    }
}

my @charset = ('a'..'z', 'A'..'Z', '0'..'9');
my $recurser = make_recurser(4, \@charset);
$recurser->('');

В качестве альтернативы просто:

print "$_\n" for glob(('{' . join(',', 'a'..'z', 'A'..'Z', '0'..'9') . '}') x 4);
4 голосов
/ 25 июля 2011

Вы, похоже, столкнулись с некоторыми проблемами с областями видимости.Perl очень гибок, поэтому он пытается угадать, что вы хотите, потому что вы не сказали ему, что вы хотите.Первое, чему вы научитесь, это добавить use strict; в качестве первого утверждения после Шебанга.Он укажет на переменные, которые не определены явно, а также на любые переменные, к которым обращаются до создания (помогает с ошибками и т. Д.).

Если вы сделаете свой код таким, вы 'Я пойму, почему вы получаете ваши ошибки:

sub recurse {
    ($width, $position, $base_string) = @_;

    for ($i = 0; $i < $charset_length; ++$i) {
        $base = $base_string . $charset[$i];
        if ($position < $width - 1) {
            $pos = $position + 1;
            recurse($width, $pos, $base);
        }
        # print "$base\n";
    }
    print "$position\n";
}

Это должно вывести:

3
3
3
3

Поскольку вы неправильно определяете $position с my, вы не получаетеновая переменная каждый рекурсор, вы повторно используете одну и ту же.Бросьте туда use strict; и исправьте полученные ошибки, и код должен быть хорошим.

4 голосов
/ 25 июля 2011

Это связано с областью действия переменных, вы все еще меняете те же самые переменные, когда вызываете рекурсию Ключевое слово «my» объявляет переменные, локальные для подпрограммы. (http://perl.plover.com/FAQs/Namespaces.html)

Я всегда использую perl с 'use strict;' объявил, заставив меня принять решение о области видимости переменных.

sub recurse {
  my ($width, $position, $base_string) = @_;
  for (my $i = 0; $i < $charset_length; ++$i) {
    my $base = $base_string . $charset[$i];
    if ($position < $width - 1) {
      my $pos = $position + 1;
      recurse($width, $pos, $base);
    }
    print $base;
    print " ";
  }
}
2 голосов
/ 25 июля 2011

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

Если вы не заботитесь о заказе, вы можете сгенерировать все 13 388 280 перестановок ( 'a'..'z', 'A..'Z', '0'..'9' ), взятых по четыре за один раз с модулем CPAN, Algorithm :: Permute

Вот пример того, как может выглядеть этот код.

use strict;
use warnings;
use Algorithm::Permute;

my $p = Algorithm::Permute->new( 
    [ 'a' .. 'z', 'A' .. 'Z', '0' .. '9' ], # Set of...
    4 # <---- at a time.
);

while ( my @res = $p->next ) {
    print @res, "\n";
}

Метод new() принимает массив ref, который перечисляет набор символов или список того, что нужно переставить. Второй аргумент - сколько за раз включить в перестановку. Таким образом, вы берете 62 вещи по 4 за раз. Затем используйте метод next() для итерации перестановок. Остальное - просто оформление витрин.

То же самое можно сократить до следующей строки Perl:

perl -MAlgorithm::Permute -e '$p=Algorithm::Permute->new(["a".."z","A".."Z",0..9],4);print @r, "\n" while @r=$p->next;'

Существует также раздел о перестановке, а также дополнительные примеры в perlfaq4 . Он включает в себя несколько примеров и перечисляет некоторые дополнительные модули, которые обрабатывают детали для вас. Одной из сильных сторон Perl является размер и полнота Сети архивов Perl (CPAN).

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