未在 foreach 中分配的 Perl 变量:范围问题
Perl variable not assigned in foreach: scope issues
我正在尝试将 .txt 文件中的一些分数归一化,方法是将每个可能的分数(例如 take#v#2;在我的代码中称为 $tokpossense)除以所有分数的总和一个词类型(例如 take#v;称为 $tokpos)。困难在于在处理的每一行时将词类型分组在一起,以便在找到新的词类型/$tokpos 时打印归一化分数。我使用了两个哈希和一个 if 块来实现这一点。
目前,问题似乎是 $tokpos 在第 20 行的 SumHash{$tokpos} 中未定义为键,导致被零除。但是,我相信 $tokpos 在此块的范围内得到了正确定义。到底是什么问题,我将如何最好地解决它?我也很乐意听到解决此问题的替代方法。
这是一个示例输入文件:
i#CL take#v#17 my#CL checks#n#1 to#CL the#CL bank#n#2 .#IT
Context: i#CL <target>take#v</target> my#CL checks#n to#CL the#CL bank#n
Scores for take#v
take#v#1: 17
take#v#10: 158
take#v#17: 174
Winning score: 174
Context: i#CL take#v my#CL <target>checks#n</target> to#CL the#CL bank#n .#IT
Scores for checks#n
check#n#1: 198
check#n#2: 117
check#n#3: 42
Winning score: 198
Context: take#v my#CL checks#n to#CL the#CL <target>bank#n</target> .#IT
Scores for bank#n
bank#n#1: 81
bank#n#2: 202
bank#n#3: 68
bank#n#4: 37
Winning score: 202
我的错误代码:
@files = @ARGV;
foreach $file(@files){
open(IN, $file);
@lines=<IN>;
foreach (@lines){
chomp;
#store tokpossense (eg. "take#v#1") and rawscore (eg. 4)
if (($tokpossense,$rawscore)= /^\s{4}(.+): (\d+)/) {
#split tokpossense for recombination
($tok,$pos,$sensenr)=split(/#/,$tokpossense);
#tokpos (eg. take#v) will be a unique identifier when calculating normalized score
$tokpos="$tok\#$pos";
#block for when new tokpos(word) is found in inputfile
if (defined($prevtokpos) and
($tokpos ne $prevtokpos)) {
# normalize hash: THE PROBLEM LIES IN $SumHash{$tokpos} which is returned as zero > WHY?
foreach (keys %ScoreHash) {
$normscore=$ScoreHash{$_}/$SumHash{$tokpos};
#print the results to a file
print "$_\t$ScoreHash{$_}\t$normscore\n";
}
#empty hashes
undef %ScoreHash;
undef %SumHash;
}
#prevtokpos is assigned to tokpos for condition above
$prevtokpos = $tokpos;
#store the sum of scores for a tokpos identifier for normalization
$SumHash{$tokpos}+=$rawscore;
#store the scores for a tokpossense identifier for normalization
$ScoreHash{$tokpossense}=$rawscore;
}
#skip the irrelevant lines of inputfile
else {next;}
}
}
额外信息:我正在使用 Pedersen 的 Wordnet WSD 工具进行词义消歧,该工具使用 Wordnet::Similarity::AllWords。输出文件由这个包生成,找到的分数必须标准化才能在我们的工具集中实现。
您没有为 $tokpos
分配任何内容。赋值是评论的一部分——你的编辑器中的语法高亮应该已经告诉你了。 strict 也会告诉你的。
此外,您可能应该在除法中使用 $prevtokpos
:$tokpos
是您以前没有遇到过的新值。要获取最后一个标记的输出,您必须在循环外处理它,因为没有 $tokpos
来替换它。为避免代码重复,请使用子例程来执行此操作:
#!/usr/bin/perl
use warnings;
use strict;
my %SumHash;
my %ScoreHash;
sub output {
my $token = shift;
for (keys %ScoreHash) {
my $normscore = $ScoreHash{$_} / $SumHash{$token};
print "$_\t$ScoreHash{$_}\t$normscore\n";
}
undef %ScoreHash;
undef %SumHash;
}
my $prevtokpos;
while (<DATA>){
chomp;
if (my ($tokpossense,$rawscore) = /^\s{4}(.+): (\d+)/) {
my ($tok, $pos, $sensenr) = split /#/, $tokpossense;
my $tokpos = "$tok\#$pos";
if (defined $prevtokpos && $tokpos ne $prevtokpos) {
output($prevtokpos);
}
$prevtokpos = $tokpos;
$SumHash{$tokpos} += $rawscore;
$ScoreHash{$tokpossense} = $rawscore;
}
}
output($prevtokpos);
__DATA__
i#CL take#v#17 my#CL checks#n#1 to#CL the#CL bank#n#2 .#IT
Context: i#CL <target>take#v</target> my#CL checks#n to#CL the#CL bank#n
Scores for take#v
take#v#1: 17
take#v#10: 158
take#v#17: 174
Winning score: 174
Context: i#CL take#v my#CL <target>checks#n</target> to#CL the#CL bank#n .#IT
Scores for checks#n
check#n#1: 198
check#n#2: 117
check#n#3: 42
Winning score: 198
Context: take#v my#CL checks#n to#CL the#CL <target>bank#n</target> .#IT
Scores for bank#n
bank#n#1: 81
bank#n#2: 202
bank#n#3: 68
bank#n#4: 37
Winning score: 202
您试图在 $tokpos
更改后立即打印结果,这让您感到困惑。一方面,$prevtokpos
的值是完整的,但是您试图输出 $tokpos
的数据;而且你永远不会显示 last 数据块,因为你需要更改 $tokpos
来触发输出。
积累给定文件的所有数据然后在到达文件末尾时打印它要容易得多。该程序通过保留三个值来工作
$tokpos
、$sense
和 $rawscore
数组 @results
中输出的每一行,以及 [=21] 中每个 $tokpos
值的总分=].然后只需将 @results
的内容转储到一个额外的列中,该列将每个值除以相应的总数。
use strict;
use warnings;
use 5.014; # For non-destructive substitution
for my $file ( @ARGV ) {
open my $fh, '<', $file or die $!;
my (@results, %totals);
while ( <$fh> ) {
chomp;
next unless my ($tokpos, $sense, $rawscore) = / ^ \s{4} ( [^#]+ \# [^#]+ ) \# (\d+) : \s+ (\d+) /x;
push @results, [ $tokpos, $sense, $rawscore ];
$totals{$tokpos} += $rawscore;
}
print "** $file **\n";
for my $item ( @results ) {
my ($tokpos, $sense, $rawscore) = @$item;
printf "%s\t%s\t%6.4f\n", $tokpos.$sense, $rawscore, $rawscore / $totals{$tokpos};
}
print "\n";
}
输出
** tokpos.txt **
take#v#1 17 0.0487
take#v#10 158 0.4527
take#v#17 174 0.4986
check#n#1 198 0.5546
check#n#2 117 0.3277
check#n#3 42 0.1176
bank#n#1 81 0.2088
bank#n#2 202 0.5206
bank#n#3 68 0.1753
bank#n#4 37 0.0954
我正在尝试将 .txt 文件中的一些分数归一化,方法是将每个可能的分数(例如 take#v#2;在我的代码中称为 $tokpossense)除以所有分数的总和一个词类型(例如 take#v;称为 $tokpos)。困难在于在处理的每一行时将词类型分组在一起,以便在找到新的词类型/$tokpos 时打印归一化分数。我使用了两个哈希和一个 if 块来实现这一点。
目前,问题似乎是 $tokpos 在第 20 行的 SumHash{$tokpos} 中未定义为键,导致被零除。但是,我相信 $tokpos 在此块的范围内得到了正确定义。到底是什么问题,我将如何最好地解决它?我也很乐意听到解决此问题的替代方法。
这是一个示例输入文件:
i#CL take#v#17 my#CL checks#n#1 to#CL the#CL bank#n#2 .#IT
Context: i#CL <target>take#v</target> my#CL checks#n to#CL the#CL bank#n
Scores for take#v
take#v#1: 17
take#v#10: 158
take#v#17: 174
Winning score: 174
Context: i#CL take#v my#CL <target>checks#n</target> to#CL the#CL bank#n .#IT
Scores for checks#n
check#n#1: 198
check#n#2: 117
check#n#3: 42
Winning score: 198
Context: take#v my#CL checks#n to#CL the#CL <target>bank#n</target> .#IT
Scores for bank#n
bank#n#1: 81
bank#n#2: 202
bank#n#3: 68
bank#n#4: 37
Winning score: 202
我的错误代码:
@files = @ARGV;
foreach $file(@files){
open(IN, $file);
@lines=<IN>;
foreach (@lines){
chomp;
#store tokpossense (eg. "take#v#1") and rawscore (eg. 4)
if (($tokpossense,$rawscore)= /^\s{4}(.+): (\d+)/) {
#split tokpossense for recombination
($tok,$pos,$sensenr)=split(/#/,$tokpossense);
#tokpos (eg. take#v) will be a unique identifier when calculating normalized score
$tokpos="$tok\#$pos";
#block for when new tokpos(word) is found in inputfile
if (defined($prevtokpos) and
($tokpos ne $prevtokpos)) {
# normalize hash: THE PROBLEM LIES IN $SumHash{$tokpos} which is returned as zero > WHY?
foreach (keys %ScoreHash) {
$normscore=$ScoreHash{$_}/$SumHash{$tokpos};
#print the results to a file
print "$_\t$ScoreHash{$_}\t$normscore\n";
}
#empty hashes
undef %ScoreHash;
undef %SumHash;
}
#prevtokpos is assigned to tokpos for condition above
$prevtokpos = $tokpos;
#store the sum of scores for a tokpos identifier for normalization
$SumHash{$tokpos}+=$rawscore;
#store the scores for a tokpossense identifier for normalization
$ScoreHash{$tokpossense}=$rawscore;
}
#skip the irrelevant lines of inputfile
else {next;}
}
}
额外信息:我正在使用 Pedersen 的 Wordnet WSD 工具进行词义消歧,该工具使用 Wordnet::Similarity::AllWords。输出文件由这个包生成,找到的分数必须标准化才能在我们的工具集中实现。
您没有为 $tokpos
分配任何内容。赋值是评论的一部分——你的编辑器中的语法高亮应该已经告诉你了。 strict 也会告诉你的。
此外,您可能应该在除法中使用 $prevtokpos
:$tokpos
是您以前没有遇到过的新值。要获取最后一个标记的输出,您必须在循环外处理它,因为没有 $tokpos
来替换它。为避免代码重复,请使用子例程来执行此操作:
#!/usr/bin/perl
use warnings;
use strict;
my %SumHash;
my %ScoreHash;
sub output {
my $token = shift;
for (keys %ScoreHash) {
my $normscore = $ScoreHash{$_} / $SumHash{$token};
print "$_\t$ScoreHash{$_}\t$normscore\n";
}
undef %ScoreHash;
undef %SumHash;
}
my $prevtokpos;
while (<DATA>){
chomp;
if (my ($tokpossense,$rawscore) = /^\s{4}(.+): (\d+)/) {
my ($tok, $pos, $sensenr) = split /#/, $tokpossense;
my $tokpos = "$tok\#$pos";
if (defined $prevtokpos && $tokpos ne $prevtokpos) {
output($prevtokpos);
}
$prevtokpos = $tokpos;
$SumHash{$tokpos} += $rawscore;
$ScoreHash{$tokpossense} = $rawscore;
}
}
output($prevtokpos);
__DATA__
i#CL take#v#17 my#CL checks#n#1 to#CL the#CL bank#n#2 .#IT
Context: i#CL <target>take#v</target> my#CL checks#n to#CL the#CL bank#n
Scores for take#v
take#v#1: 17
take#v#10: 158
take#v#17: 174
Winning score: 174
Context: i#CL take#v my#CL <target>checks#n</target> to#CL the#CL bank#n .#IT
Scores for checks#n
check#n#1: 198
check#n#2: 117
check#n#3: 42
Winning score: 198
Context: take#v my#CL checks#n to#CL the#CL <target>bank#n</target> .#IT
Scores for bank#n
bank#n#1: 81
bank#n#2: 202
bank#n#3: 68
bank#n#4: 37
Winning score: 202
您试图在 $tokpos
更改后立即打印结果,这让您感到困惑。一方面,$prevtokpos
的值是完整的,但是您试图输出 $tokpos
的数据;而且你永远不会显示 last 数据块,因为你需要更改 $tokpos
来触发输出。
积累给定文件的所有数据然后在到达文件末尾时打印它要容易得多。该程序通过保留三个值来工作
$tokpos
、$sense
和 $rawscore
数组 @results
中输出的每一行,以及 [=21] 中每个 $tokpos
值的总分=].然后只需将 @results
的内容转储到一个额外的列中,该列将每个值除以相应的总数。
use strict;
use warnings;
use 5.014; # For non-destructive substitution
for my $file ( @ARGV ) {
open my $fh, '<', $file or die $!;
my (@results, %totals);
while ( <$fh> ) {
chomp;
next unless my ($tokpos, $sense, $rawscore) = / ^ \s{4} ( [^#]+ \# [^#]+ ) \# (\d+) : \s+ (\d+) /x;
push @results, [ $tokpos, $sense, $rawscore ];
$totals{$tokpos} += $rawscore;
}
print "** $file **\n";
for my $item ( @results ) {
my ($tokpos, $sense, $rawscore) = @$item;
printf "%s\t%s\t%6.4f\n", $tokpos.$sense, $rawscore, $rawscore / $totals{$tokpos};
}
print "\n";
}
输出
** tokpos.txt **
take#v#1 17 0.0487
take#v#10 158 0.4527
take#v#17 174 0.4986
check#n#1 198 0.5546
check#n#2 117 0.3277
check#n#3 42 0.1176
bank#n#1 81 0.2088
bank#n#2 202 0.5206
bank#n#3 68 0.1753
bank#n#4 37 0.0954