加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

在perl中,当将子例程的返回值赋给变量时,数据是否在内存中重复?

发布时间:2020-12-15 21:46:12 所属栏目:大数据 来源:网络整理
导读:sub foo { my @return_value = (1,2);}my @receiver = foo(); 这个分配是否像perl中的任何其他分配一样?数组在内存中重复?我怀疑这个原因,因为子程序保存的数组是一次性的,复制完全是多余的.为了优化原因,将数组“链接”到@receiver是有意义的. 顺便说一句
sub foo {
    my @return_value = (1,2);
}
my @receiver = foo();

这个分配是否像perl中的任何其他分配一样?数组在内存中重复?我怀疑这个原因,因为子程序保存的数组是一次性的,复制完全是多余的.为了优化原因,将数组“链接”到@receiver是有意义的.

顺便说一句,我注意到类似的问题Perl: function returns reference or copy?,但没有得到我想要的.

而我在谈论Perl5

PS.关于perl这类主题的任何书籍或材料?

解决方法

返回的标量:lvalue subs不会被复制.

XS subs返回的标量不会被复制.

函数返回的标量(命名运算符)不会被复制.

其他潜艇返回的标量被复制.

但那是在任何任务发挥作用之前.如果将返回的值分配给变量,则将复制它们(同样,在普通Perl sub的情况下).

这意味着我的$y = sub {$x} – >();复制$x两次!

但由于优化,这并不重要.

让我们从一个未复制它们的例子开始.

$perl -le'
    sub f :lvalue { my $x = 123; print $x; $x }
    my $r = f();
    print $r;
'
SCALAR(0x465eb48)  # $x
SCALAR(0x465eb48)  # The scalar on the stack

但如果你删除:左值…

$perl -le'
    sub f { my $x = 123; print $x; $x }
    my $r = f();
    print $r;
'
SCALAR(0x17d0918)  # $x
SCALAR(0x17b1ec0)  # The scalar on the stack

更糟糕的是,人们通常会通过将标量指定给变量来进行跟进,因此会出现第二个副本.

$perl -le'
    sub f { my $x = 123; print $x; $x }
    my $r = f();   # 
    print $r;       #  > my $y = f();
    my $y = $$r;    # /
    print $y;
'
SCALAR(0x1802958)  # $x
SCALAR(0x17e3eb0)  # The scalar on the stack
SCALAR(0x18028f8)  # $y

从好的方面来说,优化分配以最小化复制字符串的成本.

XS subs和函数(命名运算符)通常返回凡人(“TEMP”)标量.这些是“死囚牢房”中的标量.如果没有任何步骤声明对它们的引用,它们将被自动销毁.

在旧版本的Perl(< 5.20)中,将凡人字符串分配给另一个标量将导致字符串缓冲区的所有权被转移以避免必须复制字符串缓冲区.例如,我的$y = lc($x);不复制lc创建的字符串;只需复制字符串指针.

$perl -MDevel::Peek -e'my $s = "abc"; Dump($s); $s = lc($s); Dump($s);'
SV = PV(0x1705840) at 0x1723768
  REFCNT = 1
  FLAGS = (PADMY,POK,IsCOW,pPOK)
  PV = 0x172d4c0 "abc"
  CUR = 3
  LEN = 10
  COW_REFCNT = 1
SV = PV(0x1705840) at 0x1723768
  REFCNT = 1
  FLAGS = (PADMY,pPOK)
  PV = 0x1730070 "abc"     <-- Note the change of address from stealing
  CUR = 3                        the buffer from the scalar returned by lc.
  LEN = 10

在较新版本的Perl(≥5.20)中,赋值运算符永远不会[1]复制字符串缓冲区.相反,较新版本的Perl使用写时复制(“COW”)机制.

$perl -MDevel::Peek -e'my $x = "abc"; my $y = $x; Dump($x); Dump($y);'
SV = PV(0x26b0530) at 0x26ce230
  REFCNT = 1
  FLAGS = (POK,pPOK)
  PV = 0x26d68a0 "abc"            <----+
  CUR = 3                                |
  LEN = 10                               |
  COW_REFCNT = 2                         +-- Same buffer (0x26d68a0)
SV = PV(0x26b05c0) at 0x26ce248          |
  REFCNT = 1                             |
  FLAGS = (POK,pPOK)               |
  PV = 0x26d68a0 "abc"            <----+
  CUR = 3
  LEN = 10
  COW_REFCNT = 2

好吧,到目前为止,我只讨论过标量.嗯,这是因为subs和函数只能返回标量[2].

在您的示例中,分配给@return_value的标量将被返回[3],复制,然后通过赋值再次复制到@receiver.

您可以通过返回对数组的引用来避免所有这些.

sub f { my @fizbobs = ...; @fizbobs }
my $fizbobs = f();

复制的唯一东西是引用,最简单的非undefined标量.

>好吧,也许永远不会.我认为字符串缓冲区中需要有一个空闲字节来保存COW计数.
>在列表上下文中,它们可以返回0,1或多个,但它们只能返回标量.
> sub的最后一个运算符是列表赋值运算符.在列表上下文中,列表赋值运算符返回其左侧(LHS)计算的标量.有关详细信息,请参阅Scalar vs List Assignment Operator.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读