索引数组时"使用未初始化的值"

Ric*_*ick 1 arrays perl join

尝试运行下面的代码时,我从Perl收到以下错误

Use of uninitialized value within @words in concatenation (.) or string...
Run Code Online (Sandbox Code Playgroud)

它引用了我尝试创建由三个单词序列组成的数组的行(以该行开头的行$trigrams).任何人都可以帮我解决问题吗?

my %hash;
my @words;
my $word;
my @trigrams;
my $i = 0;

while (<>) {

   @words = split;

   foreach $word (@words) {
      $hash{$word}++;

      # Now trying to create the distinct six-grams in the 10-K.
      $trigrams[$i] = join " ", $words[$i], $words[$i + 1], $words[$i + 2];

      print "$words[$i]\n";
      $i++;
   }
}
Run Code Online (Sandbox Code Playgroud)

Bor*_*din 5

所有发生的事情都是你从阵列的末端掉下来@words.您正在为每个元素执行循环@words,因此值$i从0到$#words,或者是数组的最后一个元素的索引.所以行

join " ", $words[$i], $words[$i + 1], $words[$i + 2];
Run Code Online (Sandbox Code Playgroud)

访问数组的最后一个元素$words[$i]和两个不存在的元素.

在这种情况下,与使用数组当前索引的任何循环一样,最简单的方法是迭代数组索引而不是内容.对于join是有效的,你需要从零开始,并在年底前两个元素停止,所以0 .. $#words-2.

使用数组切片为trigram选择三个元素也更简洁,并使用将数组插入字符串的事实,如同"@array",将执行相同的操作join ' ', @array.(更确切地说,它确实如此join $", @array,并且$"默认设置为单个空格.)

我建议这个修复.这是必要的,以use strictuse warnings在每一个Perl程序开始时,你应该使用声明所有的变量my尽可能晚.

use strict;
use warnings;

my %hash;

while (<>) {

   my @words = split;
   my @trigrams;

   for my $i (0 .. $#words - 2) {
      my $word = $words[$i];
      ++$hash{$word};

      $trigrams[$i] = "@words[$i,$i+1,$i+2]";

      print "$word\n";
   }
}
Run Code Online (Sandbox Code Playgroud)

更新

如果它不是太简洁你可能会喜欢这个

use strict;
use warnings;

my %hash;

while (<>) {
   my @words = split;
   my @trigrams = map "@words[$_,$_+1,$_+2]", 0 .. $#words-2;
}
Run Code Online (Sandbox Code Playgroud)