分配给pos字符串计数为"写入",触发副本?(在OS X上使用perl 5.26测试)
我正在写一个小的lexing实用程序.经常出现的一件事是搜索从给定偏移量开始的模式...并返回匹配的字符串(如果有的话).
为了支持反复尝试使用令牌,我需要我的功能,pos如果我们成功的话,将匹配设置在匹配之后,如果不是,我需要设置到我们开始搜索的地方.
例如
my $string = "abc";
consume($string, qr/b/, 1);
printf "%s\n", 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/, 1);
printf "%s\n", pos($string); # should print 1
Run Code Online (Sandbox Code Playgroud)
这是一个返回正确的东西但没有正确设置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;
}
Run Code Online (Sandbox Code Playgroud)
这是模块测试套件的示例测试
do {
my $str = "abc";
pos($str) = 0;
my $res = consume($str, qr/z/, 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");
};
Run Code Online (Sandbox Code Playgroud)
它失败并显示以下消息(另一个测试也失败):
# 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.
Run Code Online (Sandbox Code Playgroud)
我可以通过传入字符串引用并稍微更改API来解决此问题.这是完整性的新实现.
sub consume {
my ($str_ref, $pat, $pos) = @_;
pos($$str_ref) = $pos;
my $out = undef;
if ($$str_ref =~ $pat) {
$out = substr $$str_ref, $-[0], ($+[0] - $-[0]);
pos($$str_ref) = $+[0];
} else {
pos($$str_ref) = $pos;
}
return $out;
}
Run Code Online (Sandbox Code Playgroud)
那么,这里发生了什么?pos(...)除非我使用引用,否则为什么赋值不会传播回原始值?
ike*_*ami 11
Perl是否指定pos触发副本?
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"\0
SV = PVMG(0x1985810) at 0x196d4c8
FLAGS = (SMG,POK,IsCOW,pPOK)
PV = 0x1955140 "abcdef"\0
SV = PVMG(0x1985810) at 0x196d4c8
FLAGS = (SMG,POK,IsCOW,pPOK)
PV = 0x1955140 "abcdef"\0
SV = PVMG(0x1985810) at 0x196d4c8
FLAGS = (SMG,POK,pPOK)
PV = 0x1962360 "abcdefg"\0
Run Code Online (Sandbox Code Playgroud)
[为了便于阅读,将空白行添加到输出中.]
如IsCOW标志所示,与另一个标量(常量)$_共享其字符串buffer(PV).分配pos不会改变它.$_另一方面,附加会导致字符串缓冲区被复制(0x1955140⇒ 0x1962360,并且IsCOW标志丢失).
pos(...)除非我使用引用,否则为什么赋值不会传播回原始值?
因为如果改变一个变量($str)改变了一些其他不相关的变量($string),那将是非常糟糕的!他们可能共享一个字符串缓冲区是一个无关的实现细节.
也就是说,Perl通过引用传递,因此$_[0]是$string(参数)的别名,因此赋值pos($_[0])将改变两者pos($_[0])和pos($string)(是同一个变量).
| 归档时间: |
|
| 查看次数: |
104 次 |
| 最近记录: |