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

Perl指定pos触发副本吗?

发布时间:2020-12-15 21:42:22 所属栏目:大数据 来源:网络整理
导读:分配给字符串中的pos是否为“写入”,触发副本? (在OS X上使用perl 5.26测试) 我正在写一个小的lexing实用程序.经常出现的一件事是搜索从给定偏移开始的模式…并返回匹配的字符串(如果有的话). 为了支持反复尝试使用令牌,我需要我的函数将pos设置为在匹配之
分配给字符串中的pos是否为“写入”,触发副本? (在OS X上使用perl 5.26测试)

我正在写一个小的lexing实用程序.经常出现的一件事是搜索从给定偏移开始的模式…并返回匹配的字符串(如果有的话).

为了支持反复尝试使用令牌,我需要我的函数将pos设置为在匹配之后如果我们成功并且在我们开始搜索的地方(如果我们不是).

例如

my $string = "abc";
consume($string,qr/b/,1);
printf "%sn",pos($string); # should print 2

pos($string) = 0; # reset the pos,just to demonstrate
                  # the intended behavior when there isn't a match

consume($string,qr/z/,pos($string); # should print 1

这是一个返回正确的东西但没有正确设置pos的实现.

package TokenConsume;
use strict;
use warnings;

use Exporter qw[import];
our @EXPORT_OK = qw[consume];

sub consume {
    my ($str,$pat,$pos) = @_;
    pos($str) = $pos;
    my $out = undef;
    if ($str =~ $pat) {
        $out = substr $str,$-[0],($+[0] - $-[0]);
        pos($str) = $+[0];
    } else {
        pos($str) = $pos;
    }
    return $out;
}

这是模块测试套件的示例测试

do {
    my $str = "abc";
    pos($str) = 0;
    my $res = consume($str,1);
    is($res,undef,"non-first: failed match should capture nothing");
    is(pos($str),1,"non-first: failed match should return pos to beginning of search");
};

它失败并显示以下消息(另一个测试失败):

#   Failed test 'non-first: failed match should return pos to beginning of search'
#   at t/test_tokenconsume.t line 38.
#          got: '0'
#     expected: '1'
# Looks like you failed 2 tests of 7.

我可以通过传入字符串引用并稍微更改API来解决此问题.这是完整性的新实现.

sub consume {
    my ($str_ref,$pos) = @_;
    pos($$str_ref) = $pos;
    my $out = undef;
    if ($$str_ref =~ $pat) {
        $out = substr $$str_ref,($+[0] - $-[0]);
        pos($$str_ref) = $+[0];
    } else {
        pos($$str_ref) = $pos;
    }
    return $out;
}

那么,这里发生了什么?除非我使用引用,为什么pos(…)的赋值不会传播回原始值?

解决方法

Perl does assigning to pos trigger a copy?

Perl 5.20引入了一种写时复制机制,允许标量共享一个字符串缓冲区.

不,更改pos($str)不会触发副本.

$perl -MDevel::Peek -e'
    $_="abcdef"; Dump($_);
    pos($_) = 2; Dump($_);
    pos($_) = 3; Dump($_);
    $_ .= "g";   Dump($_);
' 2>&1 | grep -P '^(?:SV|  FLAGS|  PV)'

SV = PV(0x192ee10) at 0x196d4c8
  FLAGS = (POK,IsCOW,pPOK)
  PV = 0x1955140 "abcdef"

SV = PVMG(0x1985810) at 0x196d4c8
  FLAGS = (SMG,POK,pPOK)
  PV = 0x1962360 "abcdefg"

[为了便于阅读,将空白行添加到输出中.]

如IsCOW标志所示,$_与另一个标量(常量)共享其字符串缓冲区(PV).分配给pos并不会改变这一点.另一方面,附加到$_会导致字符串缓冲区被复制(0x1955140?0x1962360,并且IsCOW标志丢失).

Why isn’t the assignment to pos(...) propagating back to the original value unless I use a reference?

因为如果更改一个变量($str)会改变其他一些不相关的变量($string)会非常糟糕!他们可能共享字符串缓冲区是一个无关的实现细节.

也就是说,Perl通过引用传递,因此$_ [0]是$string(参数)的别名,因此赋值给pos($_ [0])会改变pos($_ [0])和pos( $string)(是同一个变量).

(编辑:李大同)

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

    推荐文章
      热点阅读