找回密码
 注册
Simdroid-非首页
楼主: qibbxxt

【讨论】正则系列

[复制链接]
发表于 2013-7-1 20:06:46 | 显示全部楼层 来自 英国
这个题目出得不好,而且作者给出的solution也未考虑全面情况。
1、题目中要对names/words的前后有明确的界定。
     仅仅有后面的说明是不够的。If a name/word is not at the end of the string, it can be followed by a white-space or a comma.
     names/words前后端到底是什么呢?只能靠约定俗成。
2、用作测试的例子也不能涵盖题目所反映的各种情况,见下面说明。
4、solution未考虑其它情况。solution 把应该算进来的排除在外。试试下面的例子。
  1. inStr= 'How about Rotor? Yes, Rotor!'
  2. output = regexpi(inStr, '(?<=(^|\W|_))(?<start>[a-zA-Z])[\w-]*?\k<start>(?=($|[\s,_]))', 'match')
复制代码
output =
     {}

    另外 solution 同样也把类似于 ‘ abc8bca' 等不符合要求的算在内。(Names/words contain only letters or dashes)
5、(?<start>[a-zA-Z]), \k<start>仅在需要输出token时才会比普通的(..), \1更显优势。用在这里觉得有些大材小用。
当然如果是仅仅针对于3个例子编写代码的话,就没有上面的问题了,呵呵。

点评

这个题目是不太严谨,作者的目的仅仅是想告诉大家\<和\>不能使用的情况,这一点目的达到了,也就可以了  发表于 2013-7-1 21:59
如果把lin兄的符号和数字加以考虑的话应该是: regexpi(in,'(?<=(^|\W|_))(?<start>[a-z])[a-z-]*?\k<start>(?=($|[\W_]))', 'match')  发表于 2013-7-1 21:02
lin兄确实缜密,此题目确实不够严谨,其中*?没有排除中间夹杂数字,后端条件也没有对符号的限制  发表于 2013-7-1 20:53

评分

1

查看全部评分

回复 不支持

使用道具 举报

发表于 2013-7-2 10:23:48 | 显示全部楼层 来自 新疆乌鲁木齐

题目8:Remove all the consonants(移除所有辅音字母)

Simdroid开发平台
本帖最后由 bainhome 于 2013-7-2 12:20 编辑

给定一个字符串,移除且只移除辅音字母。cody原链参看这里
题目原文:
  1. Remove all the consonants in the given phrase.

  2. Example:

  3. Input  s1 = 'Jack and Jill went up the hill';
  4. Output s2 is 'a a i e u e i';
复制代码
测试算例:
  1. %% 1
  2. s1 = 'Jack and Jill went up the hill';
  3. s2 = 'a a i e u e i';
  4. assert(isequal(s2,refcn(s1)))
复制代码
  1. %% 2
  2. s1 = 'I don''t want to work. I just want to bang on the drum all day.';
  3. s2 = 'I o'' a o o. I u a o a o e u a a.';
  4. assert(isequal(s2,refcn(s1)))
复制代码
这个题估计也是多解。
回复 不支持

使用道具 举报

发表于 2013-7-2 10:35:21 | 显示全部楼层 来自 新疆乌鲁木齐
本帖最后由 bainhome 于 2013-7-7 14:28 编辑

诸位给的题目都相当有挑战性,暂时整几个简单题目放松一下,我的笔记现在到第5题,论坛最后两道题目感觉和前面跨度略大,我看着都吃力,暂不总结了,抱歉。
基础题目有点少,才两个,我打算连贴三个,把基础内容补充补充,后面有我感觉不错的题目再持续更新,方便起见暂时放在本楼,前面的老版本我删掉了。


如果没猜错这是论坛第一次以题目案例形式讨论正则表达式,因此系统性,基础知识等等都要兼顾,争取形成经典讨论案例,让它成为后来者学习正则表达式的首选素材。
ps:表达式的做法各位有心,都在算例中给出了详解,先赞一个!
另外,哪位有空结合简单例子讲讲tokens、match和name参数。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×

点评

今天看了老兄的pdf一遍又一遍,精彩!独具匠心的点评,深层次剖析,很过瘾!!!  发表于 2013-7-7 10:26
还沉淀?再沉淀都结石了...呵呵  发表于 2013-7-2 22:44
tokens match 和 names 咱再一起沉淀沉淀?  发表于 2013-7-2 22:17

评分

1

查看全部评分

回复 不支持

使用道具 举报

发表于 2013-7-2 12:45:45 | 显示全部楼层 来自 北京
bainhome 发表于 2013-7-2 10:23
给定一个字符串,移除且只移除辅音字母。cody原链参看这里。
题目原文:测试算例:这个题估计也是多解。
...

非正则来一个:
  1. s1(ismember(lower(s1),setdiff(97:122,'aieou')))=[]
复制代码
setdiff(97:122,'aieou')是小写的所有的辅音字母
ismember函数来挑选s1中所有的辅音字母的index
再把相应位置的辅音字母替换为[]即可
回复 不支持

使用道具 举报

发表于 2013-7-2 13:38:29 | 显示全部楼层 来自 北京
本帖最后由 liuyalong008 于 2013-7-2 13:57 编辑

来一个正则的:
  1. s1(regexp(s1,'(?i)[^aeiou\W]'))=[]
复制代码
用match属性太多的时候,疏忽了其默认的start属性
regexp(s1,'(?i)[^aeiou\s\W]')返回的正是辅音字母的index

此例中,对于构造辅音字母的expression,分为两步:
1 排除aieou
2 排除空格\s 和其他标点符号\W
组合一起就是[^aeiou\W]

matlab help中对于构造辅音字母很经典:
  1. '(?=[a-z])[^aeiou]'
复制代码
'(?=str1)str2' 表示&,即同时满足俩条件


点评

刚才点评时没看到最后的部分,sorry。这个“&”表达式真心是不错。  发表于 2013-7-5 17:43
感觉此处“(?i)”有指定的特殊含义,仅指大小写不敏感,与pattern的(?=expr)尚有些许差异。  发表于 2013-7-5 14:35

评分

1

查看全部评分

回复 不支持

使用道具 举报

发表于 2013-7-2 18:19:01 | 显示全部楼层 来自 英国
本帖最后由 lin2009 于 2013-7-2 18:20 编辑

“liuyalong008 发表于 2013-7-2 13:38
来一个正则的:用match属性太多的时候,疏忽了其默认的start属性
regexp(s1,'(?i)[^aeiou\s\W]')返回的正是 ...”(回复的“高级模式”太难用了!)

s1(regexp(s1,'(?i)[^aeiou\W]'))=[]

删除的不仅仅是辅音字母,原因在于[^aeiou\W]不仅包含了辅音字母,也包含了数字和下划线。另外regexp 应该为regexpi。
看看下例:
s1 = ['Tiangong-1 is China''s first space station,',...
    'an experimental testbed to demonstrate orbital rendezvous and docking capabilities.'];
s1(regexpi(s1,'(?=[a-z])[^aeiou]'))=[]

s1 =
iao- i ia' i ae aio,a eeiea ee o eoae oia eeou a oi aaiiie.

matlab help中对于构造辅音字母很经典:
'(?=[a-z])[^aeiou]'
确实很经典,也更直接。

s1 = ['Tiangong-1 is China''s first space station,',...
    'an experimental testbed to demonstrate orbital rendezvous and docking capabilities.'];
s1(regexpi(s1,'(?=[a-z])[^aeiou]'))=[]

s1 =
iao-1 i ia' i ae aio,a eeiea ee o eoae oia eeou a oi aaiiie.

点评

regexp(s,'(?i)expression') 等价于regexpi  发表于 2013-7-2 21:23

评分

1

查看全部评分

回复 不支持

使用道具 举报

发表于 2013-7-2 18:33:27 | 显示全部楼层 来自 英国
regexprep的解,不过本质上一样
s2 = regexprep(s1, '(?=\w)[^aeiouAEIOU]', '');
回复 不支持

使用道具 举报

 楼主| 发表于 2013-7-3 08:47:47 | 显示全部楼层 来自 河北廊坊
  1. regexprep(s1,'(?i)[b-df-hj-np-tv-z]','');
复制代码
该说的前面已经说过了
回复 不支持

使用道具 举报

发表于 2013-7-4 01:45:48 | 显示全部楼层 来自 新疆乌鲁木齐

题目9:提取文本数字并求和

本帖最后由 bainhome 于 2013-7-4 01:49 编辑

给定的text文本中,把数字提取出来并求和。cody原链请参看:Problem 57. Summing Digits within Text
题目原文:
  1. Given a string with text and digits, add all the numbers together.

  2. Examples:

  3. Input str = '4 and 20 blackbirds baked in a pie'
  4. Output total is 24 Input str = '2 4 6 8 who do we appreciate?'
  5. Output total is 20
复制代码
测试算例1:
  1. %% 1
  2. str = '4 and 20 blackbirds baked in a pie';
  3. total = 24;
  4. assert(isequal(number_sum(str),total))
复制代码
测试算例2:
  1. %% 2
  2. str = '2 4 6 8 who do we appreciate?';
  3. total = 20;
  4. assert(isequal(number_sum(str),total))
复制代码
测试算例3:
  1. %% 3
  2. str = 'He worked at the 7-11 for $10 an hour';
  3. total = 28;
  4. assert(isequal(number_sum(str),total))
复制代码
上一道题正在编辑中,诸位相当猛,搞出这么多求解办法,这个笔记写起来简直是幸福的烦恼...
回复 不支持

使用道具 举报

发表于 2013-7-4 02:57:05 | 显示全部楼层 来自 英国
常规做法:
  1. sum(str2double(regexp(str, '(\d+)', 'match')))
复制代码
Alfonso大神的解法:
  1. str2num(regexprep(str,'[^\d]+','+0'));
复制代码
相当巧妙。
回复 不支持

使用道具 举报

发表于 2013-7-4 21:21:17 | 显示全部楼层 来自 英国
nwcwww 发表于 2013-6-20 10:31
把17和18楼的代码重新整理下吧。二者其实是一回事。我在这层写的详细些,方便还没入门的朋友。其他诸位在代 ...

原问题隔得太远了,直接引用一下:   
Build a function with two input arguments: a string and a word length (number of letters),
that outputs a vector of counts of the 26 letters of the alphabet, specific to words with a given length.

各位的代码都是下例基础上验证的。
txt = 'Hello World, from MATLAB';
n1 = 5; % n后面跟的是数字1.
   
所以17楼的代码,如果我自己正常写多半如下:
a = regexp(lower(txt),'\<\w{5}\>','match');
b = strcat(a{:});
Count_all = arrayfun(@(x) length(regexp(b, char(96+x))), 1:26);

很欣赏上面的代码,它是arrayfun和regexp这两个Matlab中比较灵活的函数相结合的典范。
qibbxxt在23#提到accumarray函数并用accumarray编写代码,但稍显复杂。
accumarray(reshape(char(regexp(lower(txt),sprintf('\\<\\w{%d}\\>',nl),'match'))-'`',[],1),1,[26,1])'

这里就用accumarray函数的另一种形式改写一下。

  1. txt = 'Hello World, from MATLAB';        % 输入参数
  2. n1 = 5;                                  % 输入参数

  3. pattern = ['\<\w{',num2str(n1),'}\>'];   % 也可用sprintf()的形式,但前者在此处更直观一些。
  4. a = regexpi(txt,pattern,'match');        % 与上同,a = {'hello',  'world'},可以写成 a = regexp(lower(txt),pattern,'match')。
  5. <font color="#8b0000">% 不知道楼主们计算代码size的原则是什么,代码分开来写与并起来写的size应该算一样,且更应提倡。</font>

  6. b = strcat(a{:});                        % 与上同,b =‘helloworld’,可以直接用 b = [a{:}];
  7. subs = arrayfun(@(x) regexpi(char(97:122),x),b);
  8. % 将匹配模式[a-z]和要查找的字符串b对调一下。返回b中的各位置字母在[a-z]匹配模式字母串中的位置。
  9. % 思路与qibbxxt一样。
  10. % subs =
  11. %  8     5    12    12    15    23    15    18    12     4

  12. Count_all = accumarray(subs',1,[26,1])'  % 统计各个位置(对应于各个字母)出现的次数。
  13. % 写成综合的形式:
  14. % Count_all = accumarray(arrayfun(@(x) regexpi(char(97:122),x),b)',1,[26,1])' % 四个嵌套函数。

  15. % qibbxxt 用sprintf(...) 引入动态参数n1,使得代码中的函数嵌套层次变多了,复杂度变大了,可读性降低了。
  16. <font color="#8b0000">% 个人观点是:代码简洁要照顾到可读性,对于大多数人来说,一行代码三四个函数嵌套顶多了。</font>

  17. % 另,22# liuyalong008 的代码应该最简洁。
  18. txt = 'Hello World, from MATLAB';        % 输入参数
  19. n1 = 5;                                  % 输入参数

  20. pattern = ['\<\w{',num2str(n1),'}\>'];  % 或写为 sprintf('\\<\\w{d}\\>',nl) 的形式;
  21. a = regexpi(txt,pattern,'match');
  22. Count_all = histc([a{:}],97:122)

复制代码

点评

Lin兄,点评的非常到位,其实sprintf完成的是同样的功能,不过我喜欢它的格式化输出,感觉不是很复杂  发表于 2013-7-5 15:40

评分

2

查看全部评分

回复 不支持

使用道具 举报

发表于 2013-7-4 21:55:54 | 显示全部楼层 来自 英国
lin2009 发表于 2013-7-4 21:21
原问题隔得太远了,直接引用一下:   
Build a function with two input arguments: a string and a word ...

size的计算是依据m parse tree的节点数。分开写代码,又或者是在额外申明变量的情况下确实会增加size:
  1. function a = sizetestbody(txt, n1)
  2.   pattern = ['\<\w{',num2str(n1),'}\>'];   
  3.   a = regexpi(txt,pattern,'match');        
  4. end

  5. %the size is 25
复制代码
  1. function a = sizetestbody(txt, n1)
  2.   ['\<\w{',num2str(n1),'}\>'];   
  3.   a = regexpi(txt,ans,'match');        
  4. end

  5. %the size is 24
复制代码
  1. function a = sizetestbody(txt, n1)
  2.   a = regexpi(txt,['\<\w{',num2str(n1),'}\>'],'match');        
  3. end

  4. %the size is 21
复制代码
  1. function ans = sizetestbody(txt, n1)
  2.   regexpi(txt,['\<\w{',num2str(n1),'}\>'],'match');        
  3. end

  4. %the size is 19
复制代码
回复 不支持

使用道具 举报

发表于 2013-7-5 20:18:15 | 显示全部楼层 来自 新疆乌鲁木齐

题目10:Trimming Spaces(修剪字符串首尾的空格)

本帖最后由 bainhome 于 2013-7-5 20:19 编辑

cody原链接参看这里
剔除且只剔除字符串首尾的多余空格(不包括其他符号)
题目原文:
  1. Given a string, remove all leading and trailing spaces (where space is defined as ASCII 32).
复制代码
测试算例1:
  1. %% 1
  2. a = 'no extra spaces';
  3. b = 'no extra spaces';
  4. assert(isequal(b,removeSpaces(a)))
复制代码
测试算例2:
  1. %% 2
  2. a = '      lots of space in front';
  3. b = 'lots of space in front';
复制代码
测试算例3:
  1. %% 3
  2. a = 'lots of space in back      ';
  3. b = 'lots of space in back';
复制代码
测试算例4:
  1. %% 4
  2. a = '      space on both sides    ';
  3. b = 'space on both sides';
复制代码
测试算例5:
  1. %% 5
  2. a = sprintf('\ttab in front, space at end    ');
  3. b = sprintf('\ttab in front, space at end');
复制代码
这道题比较“阴险”的是第5个测试算例。
前面两道题我的个人看法已在文档中做了更新(见第43#pdf文件) ,适合初学者梳理思路,各位高手一笑置之即可。
ps:第9题只有nwcwww和alfonso两位的解法吗?还有没有其他做法?感觉有点孤零零的,非常突兀哦。总而言之,我的基础题任务完成,各位估计也休息够了,期待进一步的探讨。
回复 不支持

使用道具 举报

 楼主| 发表于 2013-7-6 09:45:58 | 显示全部楼层 来自 河北廊坊
补一个第9题的
  1. sum(str2num(regexprep(str,'[^0-9]',blanks(1))))
复制代码
思路:
将非0-9的字符替换为空格
然后用str2num转化为数组
再用sum函数求和

点评

那我把你这个代码放在阿方索大神的leading前面做个宋兵甲铺垫铺垫,你货不能有意见啊,呵呵。  发表于 2013-7-6 22:04
回复 不支持

使用道具 举报

发表于 2013-7-6 11:01:33 | 显示全部楼层 来自 北京
bainhome 发表于 2013-7-4 01:45
给定的text文本中,把数字提取出来并求和。cody原链请参看:Problem 57. Summing Digits within Text
题目 ...

我也回答一个第九题的:
   另辟蹊径很有难度啊
我的和qi版类似:
  1. str(~ismember(str,[32 48:57]))=32
  2. sum(str2num(str))
复制代码
不过我的更繁琐
首先把除了空格和数字以外的字符全部转化为空格
再利用转换和求和即可

但是要补充一点的是小数是否也要考虑
那样的话就可以改成:
  1. str(~ismember(str,[32 46 48:57]))=32
  2. sum(str2num(str))
复制代码
  1. str='.2 4 6 8 who do we appreciate?'
  2. >> str(~ismember(str,[32 46 48:57]))=32
  3. sum(str2num(str))

  4. str =

  5. .2 4 6 8                     


  6. ans =

  7.                       18.2
复制代码

点评

更新至第9题,另外把你前面那个复杂字符串的look around编辑了,真比写代码麻烦多了。你看看有无错误或疏漏之处。  发表于 2013-7-6 23:37
一大早起来看到众位高手的帖子,感慨一下,周六上午不虚度啊,先仔细品味帖子  发表于 2013-7-6 11:53
所以说越往后写解法的水平越high,不能单纯以代码本身质量论英雄。现在simwe写个众人均赞誉的答案真心是个很有挑战性的活...  发表于 2013-7-6 11:06
回复 不支持

使用道具 举报

发表于 2013-7-6 12:31:37 | 显示全部楼层 来自 北京
bainhome 发表于 2013-7-5 20:18
cody原链接参看这里。
剔除且只剔除字符串首尾的多余空格(不包括其他符号)
题目原文:测试算例1:测试算例2 ...

第五题果然阴险:
主要是 \s-->Any white-space character; equivalent to   [ \f\n\r\t\v]
所以必须要吧\t给剔除掉
  1. regexprep(a,'^(?=[^\t])\s+|\s+$','')
复制代码
^(?=[^\t])\s+ 表示把除了\t以外的白色字符,^表示每行的开头
当然此pattern只限于开头是制表符,如果结尾也要限制就另说了

点评

如结尾也有限制,regexprep2次估计可以。如果单纯限定在剔除\t而不是其他思路,不知道有没有更好的办法。  发表于 2013-7-7 10:26
回复 不支持

使用道具 举报

发表于 2013-7-6 16:49:26 | 显示全部楼层 来自 英国
综合及优化一下第9楼的答案。
str = '4 and 20 blackbirds baked in a pie';

sum(str2double(regexp(str,'\d+','match')))  % \d+ 可以不用在两边加圆括号。另外时若将str2double换成str2num就不行,二者在用法上还是有些区别的。
str2num(regexprep(str,'\D+','+0')) % 将非数字字符变为+0,整个字符串变成了加法表达式,省去了显式求和步骤了,构思巧妙。这里用\D代替[^\d],更为直接。


sum(eval(sprintf('[%s]',regexprep(str,'\D+',' ')))) % 用sprintf构造成行向量的形式在求和。
sum(str2num(regexprep(str,'\D+',' ')))              % 用str2num化成行向量的形式;\D要比[^0-9]更直接,速度更快。

str(~ismember(str,[32 48:57]))=32;                  % 在ASCII字符集合中找出数字0-9,另辟蹊径,但ismember函数涉及到集合,运算耗时。
sum(str2num(str))

评分

1

查看全部评分

回复 不支持

使用道具 举报

发表于 2013-7-6 16:54:34 | 显示全部楼层 来自 英国
本帖最后由 lin2009 于 2013-7-6 18:19 编辑

第10题:
关键在用构造合适的匹配模式,仅删除字符串前后的(半角)空格(ASCII 32)。
pat = '^ *([^ ].*[^ ]) *$';  % 原字符串前后可能为半角空格。剩余部分的特征是前后都不是空格字符。
c = regexprep(a,pat,'$1');
assert(isequal(b,c))
验证:
  1. pat = '^ *([^ ].*[^ ]) *;

  2. %% 1
  3. a = 'no extra spaces';
  4. b = 'no extra spaces';
  5. c = regexprep(a,pat,'$1');
  6. assert(isequal(b,c))

  7. %% 2
  8. a = '      lots of space in front';
  9. b = 'lots of space in front';
  10. c = regexprep(a,pat,'$1');
  11. assert(isequal(b,c))

  12. %% 3
  13. a = 'lots of space in back      ';
  14. b = 'lots of space in back';
  15. c = regexprep(a,pat,'$1');
  16. assert(isequal(b,c))

  17. %% 4
  18. a = '      space on both sides    ';
  19. b = 'space on both sides';
  20. c = regexprep(a,pat,'$1');
  21. assert(isequal(b,c))

  22. %% 5
  23. a = sprintf('\ttab in front, space at end    ');
  24. b = sprintf('\ttab in front, space at end');
  25. c = regexprep(a,pat,'$1');
  26. assert(isequal(b,c))
复制代码

评分

1

查看全部评分

回复 不支持

使用道具 举报

发表于 2013-7-7 11:11:12 | 显示全部楼层 来自 北京
本帖最后由 liuyalong008 于 2013-7-7 11:39 编辑
bainhome 发表于 2013-7-5 20:18
cody原链接参看这里。
剔除且只剔除字符串首尾的多余空格(不包括其他符号)
题目原文:测试算例1:测试算例2 ...

突然觉得这个测试算例其实也是有bug的
不信试试这个:
  1. regexprep(a,'\s{2,}','')
  2. a( cumsum(deblank(a)-32 )~=0)
  3. deblank(strjust(a,'left'))
复制代码
应该增加以下算例:
  1. a = sprintf('\ttab in front, space at end\t    ')
复制代码
当然即使这样的算例,lin兄的代码也安然无恙
:lol

点评

贴了三个转空子的solution,但是里面的cumsum和strjust我还是比较喜欢的,尤其cumsum  发表于 2013-7-7 11:51
哈哈!娱乐至上。你得小心,祁彬彬这货最恨钻空子,他看到又要小宇宙爆发了  发表于 2013-7-7 11:20
回复 不支持

使用道具 举报

发表于 2013-7-7 11:22:10 | 显示全部楼层 来自 新疆乌鲁木齐
本帖最后由 liuyalong008 于 2013-7-7 13:27 编辑

我是用两个regexprep算的,这样正则判断部分的代价稍小一些。
  1. regexprep(regexprep(a,'^ *',''),' *$','')
复制代码
此题如不限定tab制表符不算空格,也可以用high level的strtrim命令——一个专门用来去首尾空格的函数。
ps:怎么我的代码里结尾标识的美元符号无法编辑,总是出现很多个?邪门,应该只有一个的,说明一下

点评

编辑模式真让人蛋疼,老是出错啊  发表于 2013-7-7 11:46
回复 不支持

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Simapps系列直播

Archiver|小黑屋|联系我们|仿真互动网 ( 京ICP备15048925号-7 )

GMT+8, 2024-9-22 20:03 , Processed in 0.044249 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表