为什么数组输出工作两次?为什么输出中的单词之间有多个 space?

Why does array output work twice? Why is there more than one space between words in the output?

我有一个 anagram 函数,它接收对单词数组的引用作为输入。该函数必须 return 一个散列,其键是从一组变位词中找到的第一个单词,值必须是对数组的引用,其中的每个元素都是该组中的一个单词,顺序为最早是在字典里遇到的。应删除集合中的相同单词,所有单词应减少为相同的大小写。

示例输入:

abc
BAc
BOOk
cab
one
Noe
rory
eon 
Yror
rrYo
Koob
BoKo
ooKB 
book
abc

示例输出:

abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb,book,abc
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb
abc: abc bac cab
book: book koob boko ookb
one: one noe eon
rory: rory yror rryo

我的脚本:

#!/usr/bin/perl

use strict;
use utf8;
no warnings;
use 5.10.0;

my @list = qw(abc BAc BOOk cab one Noe rory eon Yror rrYo Koob BoKo ooKB book abc);

sub anagram {
    my $arrayref = shift;
    my $index = 0;
    my @array;
    my @uniq;
    my @match;
    my %hash;
    my %uniq = ();
    for (my $i = 0; $i < $#$arrayref; $i++ ) {
        push @array, lc(@$arrayref[$i]);
    }
    say join ",", @array;
    @uniq = grep { !$uniq{$_}++ } @array;
    say join ",", @uniq;
    while (@uniq) {
        my @chars = split(//, @uniq[0]);
        my @indexes;
        my $regex = "[";
        for (my $i = 0; $i < scalar @chars; $i++) {
            $regex = $regex . $chars[$i];
        }
        $regex = $regex . "]{" . scalar @chars . "}";
        for (my $i = 0; $i < scalar @uniq; $i++) {
            if ($uniq[$i] =~ m/$regex/) {
               push @indexes, $i;
               $match[$index][$i] = $uniq[$i];
            }
        }
        $hash{$uniq[0]} = $match[$index];
        my $last_index = $#indexes;
        for (my $i = $last_index; $i >= 0; $i--) {
            splice @uniq, $indexes[$i], 1;
        }
        @indexes = ();
        $index++;
    }
    return \%hash;
}

anagram(\@list);
my $result = anagram(\@list);
say "$_: @{$result->{$_}}" for sort keys %$result;

我的输出:

abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb,book
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb,book
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb
abc: abc bac  cab
book: book       koob boko ookb
one: one noe  eon
rory: rory yror rryo
  1. 为什么数组的内容显示两次?
    21      say join ",", @array;
    23      say join ",", @uniq;

我的输出:

abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb,book
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb,book
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb

预期输出:

abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb,book,abc
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb
  1. 为什么我输出的单词之间有多个 space?
abc: abc bac  cab
book: book       koob boko ookb
one: one noe  eon
rory: rory yror rryo
  1. 为什么在删除重复项之前最后一个词 abc 丢失了?
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb,book
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb,book
abc,bac,book,cab,one,noe,rory,eon,yror,rryo,koob,boko,ookb
  1. Why are the contents of the arrays displayed twice?
anagram(\@list);
my $result = anagram(\@list);
  1. Why is there more than one space between words in my output?

由于数组中的元素为空。

  1. Why are the contents of the arrays displayed twice?

那是因为它们是在您的 anagram() 子例程中使用 say() 打印的,并且您调用了该子例程两次。

  1. Why is there more than one space between words in my output?

如果您使用 Data::Dumper 来显示 $resultanagram() 返回时的内容,您会看到它看起来像这样:

$VAR1 = {
          'book' => [
                      'book',
                      undef,
                      undef,
                      undef,
                      undef,
                      undef,
                      undef,
                      'koob',
                      'boko',
                      'ookb'
                    ],
          'rory' => [
                      'rory',
                      'yror',
                      'rryo'
                    ],
          'one' => [
                     'one',
                     'noe',
                     undef,
                     'eon'
                   ],
          'abc' => [
                     'abc',
                     'bac',
                     undef,
                     'cab'
                   ]
        };

看起来 undef 值与输出中的额外空格相对应。

也许你应该替换:

say "$_: @{$result->{$_}}" for sort keys %$result;

与:

say "$_: ", join ' ', grep { defined } @{$result->{$_}}
  for sort keys %$result;
  1. Why is the last word abc lost before deleting duplicates?

您的代码中有四个 for 循环。您使用三种不同的方法来编写这些循环。

for (my $i = 0; $i < $#$arrayref; $i++ )

for (my $i = 0; $i < scalar @chars; $i++)

for (my $i = 0; $i < scalar @uniq; $i++)

my $last_index = $#indexes;
for (my $i = $last_index; $i >= 0; $i--)

最后三个循环按照您的预期工作(但对于您要执行的操作而言,它们都过于复杂)。但是,第一个问题导致了您的问题。

您似乎意识到 $#arrayname 给出数组中的最后一个索引(您在第一个和最后一个循环中使用它)。但是你的第一个循环似乎有一个“off-by-one”错误。当 $i 不再小于 $#$arrayref 时,您将停止迭代循环。这省略了数组的最后一个元素——我不认为你打算这样做。如果您将第一个循环替换为:

for (my $i = 0; $i <= $#$arrayref; $i++ ) # Note: >= instead of >

然后你得到你期望的结果。

我预计您会犯此错误,因为在其他循环中您使用 @arrayname 而不是 $#arrayname。但是 @arrayname 为您提供数组中元素的 number,而不是最后一个索引。对于任何数组,@arrayname 总是比 $#arrayname.

多 1

您很少会发现像您这样使用“C 风格”for 循环的 Perl 程序员。那是因为他们很容易犯这种错误。但是,方便的是,我们有通常更容易理解的 foreach 样式循环。在这种形式中,我们只是生成一个值列表并遍历该列表。如果你正在遍历一个数组并需要它的索引值,我总是建议 $#arrayname 而不是 @arrayname

你的四个循环可以重写为:

foreach my $i (0 .. $#$arrayref)

foreach my $i (0 .. $#chars)

foreach my $i (0 .. $#uniq)

# This is slightly harder as we need the
# indexes in reverse order. So use "reverse()".
foreach my $i (reverse 0 .. $indexes)

在某些情况下,您仅使用 $i 来访问数组的元素。在这些情况下,只遍历列表本身比遍历索引更简单。例如,您的第一个循环是:

for (my $i = 0; $i < $#$arrayref; $i++ ) {
    push @array, lc(@$arrayref[$i]);
}

最好写成:

for my $el (@$arrayref) {
  push @array, lc $el;
}

甚至(因为 @array 开始是空的):

my @array = map { lc } @$arrayref;

但我承认这涉及到一些稍微深奥的 Perl 功能:-)

更新: 我注意到您的代码中有 no warnings。在编写 Perl 代码时,您应该始终打开警告。 use warnings 您的代码省略了两个警告(其中一个警告重复了几次)。

Scalar value @uniq[0] better written as $uniq[0] at anagram2 line 25.

您有:

my @chars = split(//, @uniq[0]);

数组的单个元素是标量值,因此应该使用 $ 而不是 @ 来访问。这一行应该是:

my @chars = split(//, $uniq[0]);

Use of uninitialized value in join or string at anagram2 line 51.

这是我们在上面讨论并修复的 $result 中未定义值的问题。

最好始终保持 use warnings 开启并解决它向您显示的任何问题。

请调查以下代码片段是否符合您的问题。

use strict;
use warnings;

my($words,$seen);

while( <DATA> ) {
    chomp;
    s/^\s+|\s+\z//;
    my $val = lc $_;
    my $key = join('', sort split('', $val));
    push @{$words->{$key}}, $val unless $seen->{$val}++;
}

printf "%-5s : %s\n", $words->{$_}[0], join(', ', @{$words->{$_}})
    for sort keys %{$words};

exit 0;

__DATA__
abc
BAc
BOOk
cab
one
Noe
rory
eon 
Yror
rrYo
Koob
BoKo
ooKB 
book
abc

输出

abc   : abc, bac, cab
book  : book, koob, boko, ookb
one   : one, noe, eon
rory  : rory, yror, rryo