分配给字符串中的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/,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"\0 SV = PVMG(0x1985810) at 0x196d4c8 FLAGS = (SMG,POK,pPOK) PV = 0x1962360 "abcdefg"\0
如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)(是同一个变量).