Chinaunix

标题: 函数在返回数组的时候遇到了问题。。。大家都来看看:) [打印本页]

作者: py    时间: 2010-08-04 15:11
标题: 函数在返回数组的时候遇到了问题。。。大家都来看看:)
我自认为这方面弄的挺明白的,没想今天出这么个问题。。。非想弄明白了这个foreach和shift操作上的差别。

当函数返回数组引用的时候foreach和shift操作起来是一样的:
  1. sub function {
  2.     my @temp = qw/a b c/;
  3.     return \@temp;
  4. }

  5. foreach my $var (@{&function}) {
  6.     print "$var\n";
  7. }

  8. my $one = shift (@{&function});
  9. print $one;
复制代码
当函数返回数组的时候shift就会出问题,提示:
Type of arg 1 to shift must be array (not subroutine entry)

代码:
  1. sub function {
  2.     my @temp = qw/a b c/;
  3.     return @temp;
  4. }

  5. foreach my $var (&function) {
  6.     print "$var\n";
  7. }

  8. my $one = shift (&function);
  9. print $one;
复制代码
从错误提示上看我需要做的应该是提示Perl传给shift的参数是一个数组。
我想了各种办法都没成功。。。

请大家给掌掌眼~~
作者: toniz    时间: 2010-08-04 15:30
回复 1# py


    :emn1: 因为返回的是一个参数列表,而不是一个array.
  1. foreach my $var qw/1 2 3/ {

  2.     print "$var\n";

  3. }


  4. my $one = shift ( qw/1 2 3/);
复制代码

作者: yybmsrs    时间: 2010-08-04 16:01
看了下文档return返回的是列表,shift要的是real ARRAY
作者: dugu072_cu    时间: 2010-08-04 16:16
可以这样:
shift( @{ [ &func ] } );
作者: py    时间: 2010-08-04 21:09
回复 4# dugu072_cu


谢谢。

我试了好多这样的写法都没写对,你写的是对的。这正是我和自己较劲想得到的答案。

这样写就强制将一个匿名列表转换成了一个real array。
作者: py    时间: 2010-08-04 21:21
回复 2# toniz


恩,对,函数返回的的确是参数列表,比如,函数可以返回两个参数,第一个参数是一个数组,第二个是一个哈希表。

那foreach怎么就能正确处理函数的返回值呢?函数的返回值如果是一个数组的时候和真实的数组有什么差别?


。。。。我再研究研究,总结一下。
作者: 黑色阳光_cu    时间: 2010-08-04 21:55
本帖最后由 黑色阳光_cu 于 2010-08-04 21:56 编辑

function 返回的是数组不是列表,shift那个报错发生在编译时,编译时,shift的短视,让它并不知道&function()能返回一个数组,所以报错。
作者: py    时间: 2010-08-05 08:26
说返回“列表”还是返回“数组”,这其实是一样的,但严格意义上说,我觉得应该说是返回了列表.
列表是数据,return @temp和return qw/a b c/无异。
文档中关于return部分也写到:“Evaluation of EXPR may be in list, scalar, or void context, depending on how the return value will be used”

shift实际是个函数,和map一样,在识别参数的时候都有局限性,我开始就是觉得是shift的问题,所以才想尽办法用类似“强制告诉shift我那function返回的是个数组”,其实我想要的就是这个写法“shift( @{ [ &func ] } )”
我本该想到的。。。以前对哈希表做过类似的事。

我再多说一句。@{ [ &function ] }的意思,里面的中括号就是强制数据为列表的形式,之后的大括号类似${abc},就是标识一下变量。前面再加上@也就真的成了一个数组了,这样shift函数就能认识了。
相应的,如果是强制数据以哈希表的形式出现,那就是%{ { &function } },这个我尝试过,是正确的。
作者: 黑色阳光_cu    时间: 2010-08-05 08:58
说返回“列表”还是返回“数组”,这其实是一样的,但严格意义上说,我觉得应该说是返回了列表.
列表是数据 ...
py 发表于 2010-08-05 08:26



   

#!/bin/env perl

use strict;
use warnings;

sub func_array
{
        my @temp = qw/a b c/;
        return @temp;
}

sub func_list
{
        return qw/a b c/;
}

my $s = scalar(func_array());
my $s2 = scalar(func_list());

warn $s;
warn $s2;


返回array或者list是有区别的
作者: flw    时间: 2010-08-05 09:13
继续弄,我看大家都还没弄明白。
作者: snriyt    时间: 2010-08-05 09:16
真复杂
作者: 黑色阳光_cu    时间: 2010-08-05 09:52

  1. #!/bin/env perl

  2. use strict;
  3. use warnings;

  4. sub my_shift(\@)
  5. {
  6.         my ($elem, @tail) = @{$_[0]};
  7.        
  8.         @{$_[0]} = @tail;
  9.         return $elem;
  10. }

  11. sub function
  12. {
  13.         my @temp = qw/a b c/;
  14.         return @temp;
  15. }


  16. warn my_shift(function());
复制代码
类似于这样子。。。
作者: hwxo    时间: 2010-08-05 10:07
foreach my $var (&function) {

my $one = shift (&function);

funciton函数均在标量环境中调用。
所以会报错。
作者: toniz    时间: 2010-08-05 11:21
  1. A "return" statement may be used to exit a subroutine, optionally specifying the returned value, which
  2.        will be evaluated in the appropriate context (list, scalar, or void) depending on the context of the sub-
  3.        routine call.  If you specify no return value, the subroutine returns an empty list in list context, the
  4.        undefined value in scalar context, or nothing in void context.  If you return one or more aggregates
  5.        (arrays and hashes), these will be flattened together into one large indistinguishable list.
复制代码
因为函数返回的是什么决定于调用该函数的环境。也就是说:
用$aa=$fun()的时候,这个是标量环境,那么函数返回的是数组或者列表处于标量环境的值,而不是返回这整个数组,或列表,然后再求值。
作者: py    时间: 2010-08-05 11:34
#!/bin/env perl

use strict;
use warnings;

sub func_array
{
        my @temp = qw/a b ...
黑色阳光_cu 发表于 2010-08-05 08:58


这就是标量上下文和列表上下文的问题了。
我的理解是这样的,如果把一个list存起来,那就是一个array,比如@name = qw/a b c/中,qw/a b c/是list,@name就是array。
qw/a b c/是('a','b','c'),你用scalar(func_list())相当于scalar qw/a b c/,这个scalar是一元的,它会对a,b,c分别做scalar,然后在标量上下文中返回scalar(c)。
作者: py    时间: 2010-08-05 11:35
回复 10# flw


我把标题写成那样就是想让你来给我个"大背跨",呵呵。很多概念我还是模糊。。。你快给说说吧。。。
作者: 黑色阳光_cu    时间: 2010-08-05 12:10
本帖最后由 黑色阳光_cu 于 2010-08-05 12:12 编辑



我错了。把Perl的上下文忘了。

但那个报错,确实是发生在编译时~~~~
作者: py    时间: 2010-08-05 13:01
我错了。把Perl的上下文忘了。

但那个报错,确实是发生在编译时~~~~
黑色阳光_cu 发表于 2010-08-05 12:10



恩,对,只要打开-w选项,运行程序的时候是能看到提示的。

Type of arg 1 to shift must be array (not subroutine entry) at ./function.pl line 17, near "&function)"
Execution of ./function.pl aborted due to compilation errors.
作者: 黑色阳光_cu    时间: 2010-08-05 13:39
本帖最后由 黑色阳光_cu 于 2010-08-05 14:24 编辑

函数被调用,传递的参数

func (1, 2, 3, 5, 6, @vals);

其实是list,而函数的返回值

return @array
return @hash

假如没有scalar上下文,都是返回list。

也就是说,函数可以返回一个或者多个值,而不是array或者hash什么的。



刚想到一个返回array的办法~~~~
作者: dugu072_cu    时间: 2010-08-05 14:40
ft~
有这么麻烦嘛,与什么上下文无关,就一点: list是 value,array 是 variable!
由于 shift 要修改传递的参数,故只能对  variable 进行操作, 而 list 是一个 value,相当于一个常量,当然无法传递给 shift 了……
作者: 黑色阳光_cu    时间: 2010-08-05 14:49
  1. sub func
  2. {
  3.         my @ar = qw/a b c/;
  4.         return @ar;
  5. }


  6. sub func2 :lvalue
  7. {
  8.         my @ar = qw/a b c/;
  9.         @ar;
  10. }

  11. warn \func();
  12. warn \func2();
复制代码

作者: py    时间: 2010-08-05 14:55
ft~
有这么麻烦嘛,与什么上下文无关,就一点: list是 value,array 是 variable!
由于 shift 要修改传 ...
dugu072_cu 发表于 2010-08-05 14:40



说“上下文”的时候,说的是另一个问题,是在说scalar的输出结果为什么会不同。

你这个解释很有道理,呵呵。。。

之前我一直认为foreach的行为就相当于shift的行为,但显然不是。。。 shift就是pop,是要对数组进行修改的,foreach只是读而已。

试了下,shift (qw/a b c/)是会报错的,提示shift must be array (not list)
作者: 黑色阳光_cu    时间: 2010-08-05 14:57
正如我所料

sub func2 :lvalue
{
        my @ar = qw/a b c/;
        @ar;
}

虽然func2是返回array了,但是

shift(func());

还是报错,因为编译时不能确定func2返回什么。
作者: toniz    时间: 2010-08-05 15:44
黑色阳光_cu 是对的。编译不通过是因为:
shift(func()); 失败是因为shift的参数只能是array.而不是一个表达式, 而func()是一个表达式。


不是返回参数能不能修改的原因。下面例子可以解释:
  1. foreach my $tt(qw(1 3 5)){
  2. print $tt."\n";
  3. #$tt=1;
  4. }
复制代码
去掉注释就运行失败,因为qw是value,是只读的。
但函数返回值并不是只读的。看下面例子:
  1. sub function {
  2.     my @temp = qw/a b c/;
  3.     return @temp;
  4. }
  5. foreach my $var(function) {
  6.     print "$var\n";
  7.     $var=1;
  8. }
复制代码

作者: 黑色阳光_cu    时间: 2010-08-05 15:54
顺便说一下 ..  产生的列表不是只读的

foreach my $elem (1 .. 10)
{
        $elem = 1;
}
作者: toniz    时间: 2010-08-05 16:08
:emn1:不应该说表达式  应该说函数调用 o(∩_∩)o...
作者: dugu072_cu    时间: 2010-08-05 17:02
黑色阳光_cu 是对的。编译不通过是因为:
shift(func()); 失败是因为shift的参数只能是array.而不是一个表 ...
toniz 发表于 2010-08-05 15:44



    黑色阳光_cu 的 warn \func(); ,说明了 return 确实是返回 list;但结合你给的代码,反而很难理解了:为什么 return 回来的 list,这里却可以修改?
作者: toniz    时间: 2010-08-05 17:15
因为作为参数传递的是临时变量。
作者: ttcn_cu    时间: 2010-08-05 18:02
Perl 内部做了很NB的优化,有点看不懂了

  1. warn "======qw======";
  2. for (qw(1 2 3 4)){
  3. warn $_;
  4. warn \$_;
  5. }
  6. warn "========..======";
  7. for (1..4){
  8. warn $_;
  9. warn \$_;
  10. }
复制代码
  1. ./x.pl
  2. ======qw====== at ./x.pl line 9.
  3. 1 at ./x.pl line 11.
  4. SCALAR(0x8166c28) at ./x.pl line 12.
  5. 2 at ./x.pl line 11.
  6. SCALAR(0x8166d48) at ./x.pl line 12.
  7. 3 at ./x.pl line 11.
  8. SCALAR(0x8166c28) at ./x.pl line 12.
  9. 4 at ./x.pl line 11.
  10. SCALAR(0x8166d48) at ./x.pl line 12.
  11. ========..====== at ./x.pl line 14.
  12. 1 at ./x.pl line 16.
  13. SCALAR(0x81677ec) at ./x.pl line 17.
  14. 2 at ./x.pl line 16.
  15. SCALAR(0x81677ec) at ./x.pl line 17.
  16. 3 at ./x.pl line 16.
  17. SCALAR(0x81677ec) at ./x.pl line 17.
  18. 4 at ./x.pl line 16.
  19. SCALAR(0x81677ec) at ./x.pl line 17.
复制代码

作者: yybmsrs    时间: 2010-08-05 18:06
回复 24# toniz


    为什么qw是value呢?qw(1 2 3)和(1..3)的区别呢?
作者: toniz    时间: 2010-08-06 09:01
这里要说到一个机制,记得数组@aa=(1..1000000000)这样会占用很大的存储空间吗?
如果我们用for(1..100000000000)却不存在这问题。这是因为:
but you should be aware that the ".." operator creates an array of all integers in the range.  This can
       take a lot of memory for large ranges.  Instead use:

           @results = ();
           for ($i=5; $i < 500_005; $i++) {
               push(@results, some_func($i));
           }

       This situation has been fixed in Perl5.005. Use of ".." in a "for" loop will iterate over the range, with-
       out creating the entire range.

           for my $i (5 .. 500_005) {
               push(@results, some_func($i));
           }

       will not create a list of 500,000 integers.


Perl在5.005.版本之后对for做了优化。所以只占用一个地址位。


而for (qw(1 2 3 4)){..}这个之所以会占用两个地址,是因为perl的内存回收机制。
作者: py    时间: 2010-08-06 09:19
正如我所料

sub func2 :lvalue
{
        my @ar = qw/a b c/;
        @ar;
}

虽然func2是返回array了,但是
...
黑色阳光_cu 发表于 2010-08-05 14:57


我觉得,一个正常的函数在做return @array;的时候就是返回了一个数组。
不能说func2返回的不是数组,但是,我认为lvalue的行为是特殊,具体可以看文档perlsub部分。
按照文档所说,lvalue出现的意义是“It is possible to return a modifiable value from a subroutine.”

  1. my $val;
  2. sub canmod : lvalue {
  3.         $val;
  4. }
  5. sub nomod {
  6.         $val;
  7. }

  8. canmod() = 5; # assigns to $val
  9. nomod() = 5; # ERROR
复制代码
运行的时候提示Can't modify non-lvalue subroutine call in scalar assignment
这么看来一般函数的返回值,在scalar context下是不能修改的。

shift(func());报错是在“编译期间”报错,这个是非常明确的,perl会清楚的提示“aborted due to compilation errors”
既然如此,那shift的报错就和很多细节都没关系了,可能词法分析就没通过,所以要用dugu072_cu说的shift( @{ [ &func ] } );

黑色阳光_cu 是对的。编译不通过是因为:
shift(func()); 失败是因为shift的参数只能是array.而不是一个表 ...
toniz 发表于 2010-08-05 15:44

qw/1 2 3/是常量,铁定不能修改的。
我也觉得函数的返回值是可以修改的。lvalue的例子只说明在scalar context下不能修改。

黑色阳光_cu 的 warn \func(); ,说明了 return 确实是返回 list;但结合你给的代码,反而很难理 ...
dugu072_cu 发表于 2010-08-05 17:02

当return @abc;的时候,返回值是array。

回复  toniz


    为什么qw是value呢?qw(1 2 3)和(1..3)的区别呢?
yybmsrs 发表于 2010-08-05 18:06

看见这样的结果真让人难以接受。。。
我觉得这俩东西是一样的。但看来还是有区别,qw(1 2 3)是常量。(1..3)是产生的list,为什么就不算常量了...?
作者: toniz    时间: 2010-08-06 09:20
在for语句中不算,但拿出来赋值还是算
作者: toniz    时间: 2010-08-06 09:21
这里只是因为PERL针对FOR语句的优化
作者: dugu072_cu    时间: 2010-08-06 09:40
我觉得,一个正常的函数在做return @array;的时候就是返回了一个数组。
不能说func2返回的不是数组,但 ...
py 发表于 2010-08-06 09:19



    perldoc -q "a list and an array"

What is the difference between a list and an array?
  An array has a changeable length. A list does not. An array is something
  you can push or pop, while a list is a set of values. Some people make
  the distinction that a list is a value while an array is a variable.
  Subroutines are passed and return lists, you put things into list
  context, you initialize arrays with lists, and you "foreach()" across a
  list. "@" variables are arrays, anonymous arrays are arrays, arrays in
  scalar context behave like the number of elements in them, subroutines
  access their arguments through the array @_, and "push"/"pop"/"shift"
  only work on arrays.

  As a side note, there's no such thing as a list in scalar context. When
  you say

          $scalar = (2, 5, 7, 9);

  you're using the comma operator in scalar context, so it uses the scalar
  comma operator. There never was a list there at all! This causes the
  last value to be returned: 9.

我是看过这段话,所以一直记得,return 的是 list ……
作者: py    时间: 2010-08-06 09:54
perldoc -q "a list and an array"

What is the difference between a list and an array?
  ...
dugu072_cu 发表于 2010-08-06 09:40


恩。。。文档中关于return里也是这么说的,说返回的是list
你看这篇http://japhy.perlmonk.org/articles/pm/2000-02.html
最后的Return Values有例子在解释,我觉得说的也有道理。
作者: toniz    时间: 2010-08-06 09:54
本帖最后由 toniz 于 2010-08-06 10:04 编辑

我也来提个问题,看下面的代码:
  1. for my $aa(qw(1 2 3 4)){
  2. #        $aa=1234;
  3.         ${\$aa}=1234;
  4. }
复制代码
这样运行正常。去掉第一句的注释,就报错。。

$aa是只读的,而\$aa指向的变量却是可写的。
也就是说\$aa并不是$aa的引用。晕不。。。
作者: py    时间: 2010-08-06 11:25
我也来提个问题,看下面的代码:这样运行正常。去掉第一句的注释,就报错。。

$aa是只读的,而\$aa指向的 ...
toniz 发表于 2010-08-06 09:54



\$aa为什么不是$aa的引用?
  1. foreach my $aa(qw(1 2 3 4)){
  2.     my $bb = \$aa;
  3.     $bb = 10;
  4.     print "$bb\n";
  5. }
复制代码
$bb是$aa的引用,所以$bb肯定是可写的。
作者: toniz    时间: 2010-08-06 11:31
本帖最后由 toniz 于 2010-08-06 11:36 编辑

不一样的, ${\$aa}=1234;是想对$aa赋值。
而你的 $bb = 10;是对$b赋值。

看下面代码:
  1. $aa=123;
  2. ${\$aa}=1;
  3. print "$aa\n";

  4. $aa=123;
  5. $bb=\$aa;
  6. $bb=1;
  7. print "$aa\n";
复制代码

作者: py    时间: 2010-08-06 13:07
不一样的, ${\$aa}=1234;是想对$aa赋值。
而你的 $bb = 10;是对$b赋值。

看下面代码:
toniz 发表于 2010-08-06 11:31



是的,是我错了。

我又仔细看了你之前的代码

  1. for my $aa(qw(2)){
  2.         ${\$aa}=1234;
  3. }
复制代码
${\$aa}和$aa是完全不一样的
$aa是:
SV = PV(0x13df917 at 0x13cf7fc0
  REFCNT = 2
  FLAGS = (PADBUSY,PADTMP,POK,READONLY,pPOK)
  PV = 0x13d1b820 "2"\0
  CUR = 1
  LEN = 8
${\$aa}是:
SV = PV(0x13cf8160) at 0x13cf72a0
  REFCNT = 1
  FLAGS = (POK,pPOK)
  PV = 0x13df5b20 "2"\0
  CUR = 1
  LEN = 8
由于$aa是在for循环里的列表中一个常量值的引用,所以reference count已经是2了,而且已经被标记为READONLY,是不可写的。
但${\$aa}没有强制READONLY,所以可以赋值。但赋值是没起作用的,$aa永远不变。
作者: toniz    时间: 2010-08-06 13:59
本帖最后由 toniz 于 2010-08-06 14:05 编辑

所以我说这里引用失效了呗。
\$aa的引用不是指向$aa。
至于原因,还真没找到相关说明。。。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2