2012-02-09 46 views
3

我尝试使用下面的Perl的功能,但我不看好三点很明确:三个问题在现有的Perl子

sub inittwiddle { 
    my ($m,$n,$aref) = @_; 
    my $i; 
    $$aref[0] = $n+1; 
    for ($i=1; $i != $n-$m+1; $i++) { 
     $$aref[$i] = 0; 
    } 
    while($i != $n+1) { 
     $$aref[$i] = $i+$m-$n; 
     $i++; 
    } 
    $$aref[$n+1] = -2; 
    $$aref[1] = 1 if ($m == 0); 
} 

首先,这是什么my ($m,$n,$aref) = @_;立场?

其次,如何理解$$$$aref[0] = $n+1;

......而这个函数被调用为inittwiddle($M,$N,\@p);什么\ @p代表不?

回答

8
  1. @_是传递给函数的参数列表。通过执行my ($m,$n,$aref) = @_;,您将$_[0]指定为$m,$_[1]$n$_[2]$aref

  2. $aref是一个标量值,它持有对数组的引用。要引用数组内的元素,您可以通过$aref->[0](这更为习惯)或通过取消引用数组引用来访问它们。通过在前面添加@,您可以参考该阵列(即@$aref)。但是,您需要数组中的第一个元素,它是一个标量,所以它通过$$aref[0]获得。添加括号(${$aref}[0])或使用箭头符号($aref->[0])澄清这一点。

  3. \@p是对数组@p的引用。由于你的函数需要一个标量作为第三个参数,所以你必须传入一个标量。 \@p就是这样。当您将数组引用传递到像这样的函数时,请注意,对数组的任何更改(如执行$$aref[0] = $n+1)都是对原始数组的更改。如果您想避免这种情况,您可以将数组解除引用到临时数组中,可能通过在该函数内执行my @tmparr = @$aref;

+1

对于初学者来说,注意由于'@ p'是通过引用传入的,所以'$$ aref []]的赋值在'@ p'数组中被赋值。 – 2012-02-09 22:26:23

+0

@EricStrom:这是一个很好的观点。我已经扩大了#3以覆盖这一点。 – CanSpice 2012-02-09 22:34:16

+1

差不多。 '@ _'表示传递给函数的参数列表,但它不是单词Perl意义上的“列表”,而是一个数组。 '@ _'指的是一个数组,其值*是参数列表中的值,即不是*参数列表中的值的副本,它们*是参数列表中的值。所以如果你用'foo($ a)'调用一个sub,那么在sub内部,'$ _ [0]'指的是与'$ a'在外部相同的标量值,也就是说, $ _ [0]'和外部的'$ a'是同一个标量值的别名。 – zgpmax 2012-02-10 12:05:48

1

第一个问题只是参数传递。当调用perl子时,它的参数在特殊数组@_中。第一行只需要数组中的前3个项目,并将它们放入这3个局部变量中。

另外两个问题涉及引用。我建议看看perlref manpage\@p正在传递参考数组@p。然后,该参考被放入$aref中,然后该数组的第一个元素被访问,其中$$aref[0](其可能更清楚地被读为${$aref}[0])。

2

首先,这是什么my ($m,$n,$aref) = @_;立场?

当您通过一组参数到一个子程序,他们正在为列表传递(有时称为数组)到一个名为@_特殊的数组。

在这种情况下,子例程将变量$m,$n$aref设置为此特殊数组中的前三个值。

它类似于此:

my $m = @_[0]; 
my $n = @_[1]; 
my $aref = @_[2]; 

其次,如何理解$$讨价还价像$$aref[0] = $n+1;

现在,我们正在进入引用。参考是指向另一个Perl变量的指针。想象一下,我有一个数组是这样的:

@my_list = ("one", "two", "three", "four"); 

我可以这样创建一个指向该列表:

my $my_list_ref = \@my_list; 

变量$my_list_ref现在指向@my_list。变量@my_list之前的反斜杠告诉Perl,我不想让$my_list_ref等于@my_list。相反,我只想要$my_list_ref指向@my_list

现在,如果我要实际参考$my_list_ref指向的数据,我会做所谓的取消引用它。我这样做,通过把一个@在它的前面:

print join ",", @{ $my_list_ref }; #prints "one, two, three, four" 

如果我想引用一个特定的值在列表中$my_list_ref点,我可以做同样类型的语法:

print ${ $my_list_ref }[0]; #Prints 'one' 
print ${ $my_list_ref }[1]; #Prints 'two' 

将参考变量放在大括号中有助于澄清你正在做什么,但同时它可能会使分析变得更加困难。为了解决这个问题,Perl允许你做一些语法简化。

在简单情况下,你可以删除大括号:

print $$my_list_ref[0]; #Prints 'one' 
print $$my_list_ref[1]; #Prints 'two' 

注意双美元符号!这是你在子程序中看到的。

大多数时候,你会看到->语法,这是preferred coding style

print $my_list_ref->[0]; #Prints 'one' 
print $my_list_ref->[1]; #Prints 'two' 

而这个函数被调用为inittwiddle($M,$N,\@p);什么\@p立场?

现在,我们进入了问题,为什么你会在第一个地方使用引用。首先,我们来看看Perl子程序的一些局限性:

  1. 所有参数都在一个单一的@_数组中传递。如果我有一个以两个列表作为参数的子程序呢?他们都得到了单一的@_阵列。
  2. 如果我在我的子程序中传入一个非常大的列表,这可能需要很多内存,因为该列表被复制到@_阵列中。
  3. 在Perl子例程中,我传递的所有变量在子例程中都有独立的标识。在子程序中更改它们不会在离开子程序后更改它们的值。如果我想改变他们的价值呢?

使用参考可以解决所有这些问题。编写该子程序的开发人员正在试图解决问题#2和问题#3。召唤:

inittwiddle($M, $N, \@p); 

走的是两条变量,及阵列@p参考。如果数组@p中有100万个值,则可能需要很长时间才能将整个列表复制到子例程。此外,开发人员看起来像他们也在更改@p中的项目。当开发人员说:

$$aref[0] = $n + 1; 

他实际上改变$p[0]$n + 1。当子程序返回时,$p[0]将具有与子程序之前不同的值。请记住,@$aref不只是@p的副本,它指向@p,因此它是@p

有关引用的更多信息,您应该阅读Perl Reference Tutorial

+1

'标量值@更好地写成$ _ [0]','@my_list = [...]'这些应该是parens而不是方括号,'@ _'是一个不是列表的数组,这两个是相关的概念,但是不可互换。 – 2012-02-10 02:42:41

+0

@EricStrom - 我认为当我们从PERL 3.0移到Perl 4.0时,有人提到_Array_和_Associative Array_现在将被称为_Lists_和_Hashes_以帮助区分这两者。重读Perldata,我发现这些变量的正确术语是_Array_和_Hash_。我编辑了我的答案以反映这一点。 – 2012-02-10 16:22:04

+0

文档可能随时间而改变,但以下是当前文档必须说明的内容:http://perldoc.perl.org/perlfaq4.html,请参阅“列表和数组之间的区别是什么?” – 2012-02-10 16:30:40