我正在尝试使用现有的Perl程序,该程序包含GetItems
的以下功能。以下列出了调用此函数的方法。
我对这个程序有几个问题:
foreach my $ref (@_)
的目标是什么?我认为@_
应与传递的参数相关,但不太确定。
在my @items = sort { $a <=> $b } keys %items;
中,左侧的“项目”应与右侧的“项目”不同?为什么他们使用相同的名字?
$items{$items[$i]} = $i + 1;
的目标是什么?看起来它只是按顺序设置散列$items
的值。
$items = GetItems($classes, $pVectors, $nVectors, $uVectors);
######################################
sub GetItems
######################################
{
my $classes = shift;
my %items = ();
foreach my $ref (@_)
{
foreach my $id (keys %$ref)
{
foreach my $cui (keys %{$ref->{$id}}) { $items{$cui} = 1 }
}
}
my @items = sort { $a <=> $b } keys %items;
open(VAL, "> $classes.items");
for my $i (0 .. $#items)
{
print VAL "$items[$i]\n";
$items{$items[$i]} = $i + 1;
}
close VAL;
return \%items;
}
答案 0 :(得分:3)
foreach my $ref (@_)
遍历作为参数传递给GetItems的每个哈希引用。如果电话看起来像这样:
$items = GetItems($classes, $pVectors, $nVectors, $uVectors);
然后循环处理$pVector
,$nVectors
和$uVectors
中的哈希引用。
@items
和%items
是完全不同的变量!! @items
是一个数组变量,%items
是一个哈希变量。
$items{$items[$i]} = $i + 1
完全按照您的说法完成。它将密钥为%items
的{{1}}哈希值设置为$items[$i]
。
答案 1 :(得分:3)
当你输入一个函数时,@_
从作为传递给函数的所有参数(别名)的数组开始;但是my $classes = shift
删除了@_
的第一个元素并将其存储在变量$classes
中,因此foreach my $ref (@_)
会迭代所有剩余的参数,在$ref
中一次一个地存储(别名)。
标量,散列和数组都通过语法区分,因此它们可以具有相同的名称。您可以同时拥有$foo
,@foo
和%foo
,但他们不必彼此保持任何关系。 (这与$foo[0]
引用@foo
和$foo{'a'}
引用%foo
的事实一起,对语言的新手造成了很多混乱;你是并不孤单。)
完全。它将%items
的每个元素设置为一个不同的整数,范围从1到元素的数量,按键以数字(!)顺序进行。
答案 2 :(得分:0)
这是(几乎)逐行描述子程序
中发生的事情定义名为GetItems
的子<。<}。
sub GetItems {
将第一个值存储在default array @_
中,然后将其从数组中删除。
my $classes = shift;
创建一个名为%items
的新哈希。
my %items;
循环给予子程序的剩余值,将$ref
设置为每次迭代的值。
for my $ref (@_){
此代码假定前一行将$ref
设置为散列引用。它遍历$ref
引用的哈希的未排序键,将键存储在$id
中。
for my $id (keys %$ref){
使用上一行给出的密钥($id
),遍历$ref
中该位置的哈希引用的键。同时还设置$cui
。
for my $cui (keys %{$ref->{$id}}) {
将位置%item
的{{1}}的值设置为$cui
。
1
前一行的循环结束。
$items{$cui} = 1;
根据数值,在 }
}
}
中存储%items
的{{3}}个键列表。
@items
打开 my @items = sort { $a <=> $b } keys %items;
命名的文件,并附上$classes
。这使用了sort的旧式两个arg形式。它也会忽略open
的返回值,因此即使出错也会继续到下一行。它将文件句柄存储在全局.items
。
*VAL{IO}
循环遍历 open(VAL, "> $classes.items");
的索引列表。
@items
open
该索引上的值,它自己的行 for my $i (0 .. $#items){
。
*VAL{IO}
使用相同的值作为索引加{1}的 print VAL "$items[$i]\n";
(它是键的索引)。
%items
循环结束。
$items{$items[$i]} = $i + 1;
Print文件句柄 }
。
*VAL{IO}
返回对哈希 close VAL;
的引用。
%items
子程序结束。
return \%items;
答案 3 :(得分:0)
我对这个程序有几个问题:
foreach my $ref (@_)
的目标是什么?我认为@_应该与传递的参数有关,但不太确定。
是的,你是对的。将参数传递给子例程时,它们会自动放入@_
数组中。 (在Perl中称为列表)。 foreach my $ref (@_)
开始循环。将对@_
数组中的每个项重复此循环,并且每次$ref
的值都将分配给数组中的下一个项目。有关for
loops and foreach
loops的信息,请参阅Perldoc的Perlsyn(Perl语法)部分。另请参阅General variables的Perldoc的Perlvar(Perl变量)部分,了解有关@_
等特殊变量的信息。
现在,行my $classes = shift;
正在移除@_
列表中的第一项并将其放入变量$classes
。因此,foreach
循环将重复三次。每次$ref
首先设置为$pVectors
,$nVectors
,最后设置为$uVectors
。
顺便说一下,这些并不是真正的标量值。在Perl中,您可以拥有所谓的reference
。这是您引用的数据结构的内存位置。例如,我有五个学生,每个学生都进行了一系列测试。我想将每个测试的所有值存储在由学生ID键入的哈希值中。
通常,哈希中的每个条目只能包含一个项目。但是,如果此项指的是包含学生成绩的列表,该怎么办?
以下是学生#100成绩列表:
@grades = (100, 93, 89, 95, 74);
以下是我如何在哈希中设置学生100的条目:
$student{100} = \@grades;
现在,我可以将学生#100的一年级作为$student{100}[0]
进行讨论。请参阅Perldoc的Mark's very short tutorial about references。
- 在
my @items = sort { $a <=> $b } keys %items;
中,左侧的“项目”应与右侧的“项目”不同?为什么他们使用相同的名字?
在Perl中,您有三种主要类型的变量:列表(有些人称之为 Arrays ), Hashes (有些人称之为密钥数组)和 Scalars 。在Perl中,让不同的变量类型具有相同的名称是完全合法的。因此,您可以在程序中使用$var
,%var
和@var
,并将它们视为完全独立的变量 1 。
这通常是坏事,并且非常气馁。当您考虑单个值时会变得更糟:$var
引用标量,而$var[3]
引用列表,$var{3}
引用哈希。是的,它可能非常非常令人困惑。
在这种特殊情况下,他有一个名为%item
的哈希(一个键控数组),他将这个哈希中的键转换为按键排序的列表。此语法可以简化为:
my @items = sort { $a <=> $b } keys %items;
只是:
my @items = sort keys %items;
- $ items {$ items [$ i]} = $ i + 1;目的是什么?看起来它只是按顺序设置哈希$项的值。
让我们看看整个循环:
foreach my $i (0 .. $#items)
{
print VAL "$items[$i]\n";
$items{$items[$i]} = $i + 1;
}
对于@items
列表中的每个项,子例程将循环遍历此循环。这是旧%items
哈希的键的排序列表。 $#items
表示项目列表中的最大索引。例如,如果@items = ("foo", "bar", and "foobar")
,那么$#item
将是2
,因为此列表中的最后一项是$item[2]
,等于foobar
。
通过这种方式,他正在点击@items
中每个条目的索引。 (记住:这与%item
不同!)。
下一行有点棘手:
$items{$items[$i]} = $i + 1;
请记住,$item{}
是指旧%items
哈希!他正在创建一个新的%items
哈希。这是由@items
列表中每个项目键入的。并且,该值被设置为该项的索引加1.让我们假设:
@items = ("foo", "bar", "foobar")
最后,他正在这样做:
$item{foo} = 1;
$item{bar} = 2;
$item{foobar} = 3;
1 嗯,这不是100%真实。 Perl将每个变量存储在一种哈希结构中。在内存中,$var
,@var
和%var
将存储在内存中的相同哈希条目中,但存储在与每个变量类型相关的位置中。 99.9999%的时间,这不重要。就你而言,这是三个完全不同的变量。
然而,有一些罕见的情况,当程序员在Perl中直接使用内存时会利用这一点。
答案 4 :(得分:0)
我想告诉你我将如何编写该子程序 首先,我想向您展示一些如何以及为什么更改代码的步骤。
for
循环次数:首先关闭此循环不需要将$items{$cui}
的值设置为特定的任何值。它也不一定是一个循环。
foreach my $cui (keys %{$ref->{$id}}) { $items{$cui} = 1 }
这几乎是一回事。唯一真正的区别是它将所有内容设置为undef
。
@items{ keys %{$ref->{$id}} } = ();
如果您确实需要将值设置为1
。请注意,(1)x@keys
会返回1
的{{1}}列表,其中@keys
的元素数量相同。
my @keys = keys %{$ref->{$id}};
@items{ @keys } = (1) x @keys;
如果你不得不循环遍历大量的元素,那么for
循环可能是一个好主意,但前提是你必须将值设置为undef
之外的其他值。 。因为我们只使用一次循环变量,所以做一些简单的事情;我会使用这段代码:
$items{$_} = 1 for keys %{$ref->{$id}};
keys
交换values
:在此之前,我们看到:
foreach my $id (keys %$ref){
如果您没有注意到$id
仅使用了一次,那就是获取相关值。
这意味着我们可以使用values
并删除%{$ref->{$id}}
语法。
for my $hash (values %$ref){
@items{ keys %$hash } = ();
}
($hash
不是一个好名字,但我不知道它代表什么。)
open
:不建议使用open
的两个参数形式,或盲目使用文件句柄的裸字样式。
open(VAL, "> $classes.items");
顺便说一句,你知道吗open
还有一种论证形式。我不是真的推荐它,它主要是为了向后兼容。
our $VAL = "> $classes.items";
open(VAL);
推荐的方法是使用3个参数。
open my $val, '>', "$classes.items";
可能会有一些罕见的边缘情况需要/想要使用这两个参数版本。
sub GetItems {
# this will cause open and close to die on error (in this subroutine only)
use autodie;
my $classes = shift;
my %items;
for my $vector_hash (@_){
# use values so that we don't have to use $ref->{$id}
for my $hash (values %$ref){
# create the keys in %items
@items{keys %$hash} = ();
}
}
# This assumes that the keys of %items are numbers
my @items = sort { $a <=> $b } keys %items;
# using 3 arg open
open my $output, '>', "$classes.items";
my $index; # = 0;
for $item (@items){
print {$output} $item, "\n";
$items{$item} = ++$index; # 1...
}
close $output;
return \%items;
}
最后for
循环的另一个选项。
for my $index ( 1..@items ){
my $item = $items[$index-1];
print {$output} $item, "\n";
$items{$item} = $index;
}
如果您的Perl版本是5.12或更高版本,您可以像这样编写最后一个for
循环:
while( my($index,$item) = each @items ){
print {$output} $item, "\n";
$items{$item} = $index + 1;
}