关于Perl函数的三个问题

时间:2012-01-08 22:00:27

标签: perl

我正在尝试使用现有的Perl程序,该程序包含GetItems的以下功能。以下列出了调用此函数的方法。

我对这个程序有几个问题:

  1. foreach my $ref (@_)的目标是什么?我认为@_应与传递的参数相关,但不太确定。

  2. my @items = sort { $a <=> $b } keys %items;中,左侧的“项目”应与右侧的“项目”不同?为什么他们使用相同的名字?

  3. $items{$items[$i]} = $i + 1;的目标是什么?看起来它只是按顺序设置散列$items的值。

  4. $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;
    }
    

5 个答案:

答案 0 :(得分:3)

  1. foreach my $ref (@_)遍历作为参数传递给GetItems的每个哈希引用。如果电话看起来像这样:

    $items = GetItems($classes, $pVectors, $nVectors, $uVectors);
    

    然后循环处理$pVector$nVectors$uVectors中的哈希引用。

  2. @items%items是完全不同的变量!! @items是一个数组变量,%items是一个哈希变量。

  3. $items{$items[$i]} = $i + 1完全按照您的说法完成。它将密钥为%items的{​​{1}}哈希值设置为$items[$i]

答案 1 :(得分:3)

  1. 当你输入一个函数时,@_从作为传递给函数的所有参数(别名)的数组开始;但是my $classes = shift删除了@_的第一个元素并将其存储在变量$classes中,因此foreach my $ref (@_)会迭代所有剩余的参数,在$ref中一次一个地存储(别名)。

  2. 标量,散列和数组都通过语法区分,因此它们可以具有相同的名称。您可以同时拥有$foo@foo%foo,但他们不必彼此保持任何关系。 (这与$foo[0]引用@foo$foo{'a'}引用%foo的事实一起,对语言的新手造成了很多混乱;你是并不孤单。)

  3. 完全。它将%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;

请参阅sort函数和keys函数上的Perldocs。

  
      
  • $ 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不是一个好名字,但我不知道它代表什么。)


3 arg 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;
  }