m//在列表上下文中的返回值问题



QUOTE:
原帖由 sosogh 于 2008-4-15 23:42 发表


谢谢你的回复
()里为什么是onE?
第一句返回2个值,@line有2个元素

你咋知道第一句里面返回两个值呢?
照你这个写法,第一句返回的就只有一个值
因为你的(one)*, 意思是匹配0次或者多次的one,而*是贪婪匹配,也就是说它倾向于匹配多次,所以结果就是
(one)*匹配了$_的所有内容

你可以用非贪婪匹配
see

[Copy to clipboard] [ - ]
CODE:
<lig@romeo:~>$ perl -e '$_="oneoneone";@line = m /(one)*?/g; print "@line\n"'
one  one  one

当然这里的*?有点问题,因为*?倾向于不匹配(*代表0次或者1次),所以(one)*?其实在任何时候都是匹配成功的,这样就会产生死循环,所以这个时候起作用的是regex的引擎,是这个引擎使得匹配的位置bump along

更好的写法是将*?换成+?,当然在这里结果是一样的,但是内在的处理过程却是不一样的
其实我不理解的是最后的返回结果

第一个例子中既然都匹配了,为什么返回的结果不是“OneoNeonE”呢?


QUOTE:
原帖由 churchmice 于 2008-4-16 12:25 发表

你咋知道第一句里面返回两个值呢?
...

$#line = 1 哦


QUOTE:
原帖由 andy820303 于 2008-4-16 14:15 发表
其实我不理解的是最后的返回结果

第一个例子中既然都匹配了,为什么返回的结果不是“OneoNeonE”呢?

丢脸了
发现你所说的问题所在了
see see再说
不好意思


QUOTE:
原帖由 andy820303 于 2008-4-16 14:15 发表
其实我不理解的是最后的返回结果

第一个例子中既然都匹配了,为什么返回的结果不是“OneoNeonE”呢?

因为(one)会把(one)变成一组..(one)* 自动匹配 0个以上的one....也就是one, oneone,oneoneone,都匹配....
不管有没有给//g都一样..

楼上已经解释的很清楚了.....
我还不确定是否完全理解了apile兄的意思。

不过我可以理解这个匹配,所有的内容都被匹配上了,不过我想楼主的意思应该也是想问:为什么得到的结果只有最后的onE呢?


QUOTE:
原帖由 andy820303 于 2008-4-16 14:15 发表
其实我不理解的是最后的返回结果

第一个例子中既然都匹配了,为什么返回的结果不是“OneoNeonE”呢?

我想我已经理解了
首先看一段perldoc

QUOTE:
The "/g" modifier specifies global pattern matching--that is,
               matching as many times as possible within the string.  How it
               behaves depends on the context.  In list context, it returns a
               list of the substrings matched by any capturing parentheses in
               the regular expression.  If there are no parentheses, it
               returns a list of all the matched strings, as if there were
               parentheses around the whole pattern.

这段话的意思就是说m//g的返回值是由()捕获到的值,如果没有()的话则返回所有匹配的内容

你第一句话的错误就在于你用()把one给扩起来了,这样m/(one)*/g的返回值就只能是()捕获到的值,也就是说是one
你可以选用non capture parenthess,

[Copy to clipboard] [ - ]
CODE:
(?:one)*

这样结果就是正确的

[Copy to clipboard] [ - ]
CODE:
<lig@romeo:~/chinaunix>$ perl -e '$_="OneoNeonE";@line=m/(?:one)*/gi;print "@line\n"'
OneoNeonE

至于具体的匹配过程我是这样理解的,
1.一开始看到(one)*,所以整个$_都被匹配了,这个时候返回给@data的值是"onE",因为用了捕捉的(),所以m//g的返回内容是()中捕获的内容
2.由于整个m//g,所以引擎尝试再次匹配,由于(one)*也匹配最后的空字符(zero width match),所以这个时候返回给@data的是一个null
3.由于(one)*其实可以匹配无限次(因为它本来就匹配nothing),这样会造成死循环,引擎已经预料到这种情况,所以就退出


所以结果@data里面有两个元素,最后一个是没有初始化的元素(第二次匹配返回的null)


see

[Copy to clipboard] [ - ]
CODE:
#!/usr/bin/perl
use strict;
use warnings;
my $str = "OneoNeonE";
my @data = $str =~ m/(one)*/gi;
my $length = $#data;
print "length is $length\n";
print "$_\n" foreach @data;



QUOTE:
<lig@romeo:~/chinaunix>$ ./greedy
length is 1
onE
Use of uninitialized value in concatenation (.) or string at ./greedy line 8.

The "/g" modifier specifies global pattern matching--that is,
               matching as many times as possible within the string.  How it
               behaves depends on the context.  In list context, it returns a
               list of the substrings matched by any capturing parentheses in
               the regular expression.  If there are no parentheses, it
               returns a list of all the matched strings, as if there were
               parentheses around the whole pattern.

很精彩阿,churchmice 兄看得太仔细了,但是这种逆向的匹配说起来还是很别扭的:(one)*匹配后得到的竟然是最后一个onE,为什么要这么些呢?


QUOTE:
原帖由 andy820303 于 2008-4-16 15:12 发表
The "/g" modifier specifies global pattern matching--that is,
               matching as many times as possible within the string.  How it
               behaves depends on the context.  In lis ...

post to Larry Wall
其实只要理解了正则引擎的backtrace(回朔),这样也是可以理解的
对于$_="OneoNeonE"
使用模式(one)*/i
1.正则引擎从字符串的头部开始
2.由于(one)*也匹配空,所以引擎创建 state1,这个state包含了匹配的内容,这里就是null
3.接着看到One,也匹配,所以引擎创建 state2,这个state包含了匹配内容 One
4.接着看到oNe,创建state3,包含匹配内容 oNe
5.看到onE,创建state4,包含匹配内容onE
6.这个时候(one)*整体匹配成功了,而且regex后面也没有其他需要继续匹配的东西,因而regex引擎认为整体匹配已经成功,所以没有必要backtrace了,这个时候最新的state,也就是state4里面存放的内容是onE,所以这个结果就被返回了


这些state都是存放在类似堆栈的结构中,state1在stack的底部,state4在stack的顶部
如果regex是(one)*one的话由于(one)*匹配了所有内容导致后面的one无法匹配,因而要退栈,返回到state3,再尝试匹配,这个时候全局就匹配成功了,你可以试验一下,这个时候的返回内容就是oNe了

这就是backtrace的大致原理,当然实际中还有各种优化,远比这个复杂