Lớp Re2d, Python 2
Cập nhật: thêm vấn đề "9. Căn chỉnh".
Cách tiếp cận của tôi là sử dụng mô-đun Python để thực hiện tìm kiếm và khớp. Lớp Re2d chuẩn bị văn bản để xử lý, thực thi các hàm re và định dạng kết quả cho đầu ra.
Lưu ý rằng đây không phải là một ngôn ngữ hoàn toàn mới - đó là ngôn ngữ biểu thức chính quy tiêu chuẩn được chiếu thành 2 chiều với các cờ được thêm cho các chế độ 2D bổ sung.
Lớp học có cách sử dụng sau:
re2dobject = Re2d(<horizontal pattern>, [<vertical pattern>], [<flags>])
Cả hai mẫu đều là mẫu RE văn bản tuyến tính tiêu chuẩn. Nếu một mẫu dọc không được cung cấp, lớp cũng sẽ sử dụng mẫu ngang để khớp theo chiều dọc. Các cờ là các cờ RE tiêu chuẩn với một số phần mở rộng 2D.
Kiểm tra
1. Finding chessboards
Chessboard pattern at (2, 1, 4, 3)
print '\n1. Finding chessboards'
reob1 = Re2d('#(_#)+_?|_(#_)+#?')
found = reob1.search('~______~\n~##_#_#~\n~#_#_##~\n~##_#_#~\n~______~')
print 'Chessboard pattern at', found
assert not reob1.search('#_##\n_#_#\n__#_\n#_#_\n#_#_')
Phương pháp tìm kiếm đã tìm thấy một mẫu bàn cờ và trả về vị trí 4 tuple. Bộ dữ liệu có x,y
vị trí của nhân vật đầu tiên của trận đấu và
width, height
khu vực phù hợp. Chỉ có một mẫu được đưa ra để nó sẽ được sử dụng cho khớp ngang và dọc.
2. Verifying chessboards
Is chess? True
print '\n2. Verifying chessboards'
reob2 = Re2d('^#(_#)*_?|_(#_)*#?$')
print 'Is chess?', reob2.match('_#_#_#_#\n#_#_#_#_\n_#_#_#_#')
assert not reob2.match('_#_#_#__\n__#_#_#_\n_#_#_#__')
Bàn cờ đã được xác minh bằng phương thức khớp sẽ trả về giá trị boolean. Lưu ý rằng ^
và $
bắt đầu và ký tự cuối được yêu cầu để phù hợp với toàn bộ
văn bản.
3. Rectangle of digits
Found: [(0, 1, 5, 3), (1, 1, 4, 3), (2, 1, 3, 3), (3, 1, 2, 3), (0, 2, 5, 2), (1, 2, 4, 2), (2, 2, 3, 2), (3, 2, 2, 2), (6, 3, 2, 2)]
Not found: None
print '\n3. Rectangle of digits'
reob3 = Re2d(r'\d\d+', flags=MULTIFIND)
print 'Found:', reob3.search('hbrewvgr\n18774gwe\n84502vgv\n19844f22\ncrfegc77')
print 'Not found:', reob3.search('uv88wn000\nvgr88vg0w\nv888wrvg7\nvvg88wv77')
Bây giờ chúng tôi sử dụng MULTIFIND
cờ để trả về tất cả các kết quả khớp có thể có cho khối 2+ chữ số. Phương pháp tìm thấy 9 kết quả khớp có thể. Lưu ý rằng chúng có thể được chồng chéo.
4. Word search (orthogonal only)
Words: [(0, 0, 4, 1), (0, 3, 4, 1), (3, 3, -4, -1), (3, 2, -4, -1), (3, 0, -4, -1)] [(0, 0, 1, 4), (3, 0, 1, 4), (3, 3, -1, -4), (0, 3, -1, -4)]
Words: ['SNUG', 'WOLF', 'FLOW', 'LORE', 'GUNS'] ['S\nT\nE\nW', 'G\nO\nL\nF', 'F\nL\nO\nG', 'W\nE\nT\nS']
No words: [] []
print '\n4. Word search (orthogonal only)'
words = 'GOLF|GUNS|WOLF|FLOW|LORE|WETS|STEW|FLOG|SNUG'
flags = HORFLIP | VERFLIP | MULTIFIND
reob4a, reob4b = Re2d(words, '.', flags), Re2d('.', words, flags)
matching = 'SNUG\nTEQO\nEROL\nWOLF'
nomatch = 'ABCD\nEFGH\nIJKL\nMNOP'
print 'Words:', reob4a.search(matching), reob4b.search(matching)
print 'Words:', reob4a.findall(matching), reob4b.findall(matching)
print 'No words:', reob4a.findall(nomatch), reob4b.findall(nomatch)
Thử nghiệm này cho thấy việc sử dụng lật dọc và ngang. Điều này cho phép kết hợp các từ được đảo ngược. Từ chéo không được hỗ trợ. Các
MULTIFIND
lá cờ cho phép nhiều trận đấu chồng chéo trong tất cả 4 hướng. Phương thức findall sử dụng tìm kiếm để tìm các hộp phù hợp sau đó trích xuất các khối văn bản phù hợp. Lưu ý cách tìm kiếm sử dụng chiều rộng và / hoặc chiều cao âm cho khớp theo hướng ngược lại. Các từ theo hướng dọc có các ký tự dòng mới - điều này phù hợp với khái niệm về các khối ký tự 2D.
7. Calvins portals
Portals found: [(3, 1, 5, 6)]
Portal not found None
print '\n7. Calvins portals'
reob7 = Re2d(r'X\.{2,22}X|.X{2,22}.', r'X\.{3,22}X|.X{3,22}.', MULTIFIND)
yes = '....X......\n.XXXXXX.XX.\n...X...X...\n.X.X...XXX.\n...X...X.X.\n.XXX...X.X.\nX..XXXXX.X.'
no = 'XX..XXXX\nXX..X..X\nXX..X..X\n..X.X..X\n.X..X.XX'
print 'Portals found:', reob7.search(yes)
print 'Portal not found', reob7.search(no)
Tìm kiếm này cần các mẫu riêng biệt cho mỗi thứ nguyên vì kích thước tối thiểu là khác nhau cho mỗi thứ nguyên.
9. Alignment
Found: ['#.,##', '##'] ['#\n.\n,\n.\n#', '#\n,\n.\n#']
Found: [(3, 4, 5, 1), (6, 4, 2, 1)] [(7, 0, 1, 5), (3, 1, 1, 4)]
Not found: None None
print '\n9. Alignment'
reob9a = Re2d(r'#.*#', r'.', MULTIFIND)
reob9b = Re2d(r'.', r'#.*#', MULTIFIND)
matching = '.,.,.,.#.,\n,.,#,.,.,.\n.,.,.,.,.,\n,.,.,.,.,.\n.,.#.,##.,\n,.,.,.,.,.'
nomatch = '.,.#.,.,\n,.,.,.#.\n.,#,.,.,\n,.,.,.,#\n.#.,.,.,\n,.,.#.,.\n#,.,.,.,\n,.,.,#,.'
print 'Found:', reob9a.findall(matching), reob9b.findall(matching)
print 'Found:', reob9a.search(matching), reob9b.search(matching)
print 'Not found:', reob9a.search(nomatch), reob9b.search(nomatch)
Bộ 2 tìm kiếm này tìm thấy 2 kết quả dọc và 2 khớp ngang, nhưng không thể tìm thấy #.,#
chuỗi nhúng .
10. Collinear Points (orthogonal only)
Found: [(0, 1, 7, 1)] [(3, 1, 1, 4)]
Not found: None None
print '\n10. Collinear Points (orthogonal only)'
matching = '........\n#..#..#.\n...#....\n#.......\n...#....'
nomatch = '.#..#\n#..#.\n#....\n..#.#'
reob10h = Re2d(r'#.*#.*#', '.')
reob10v = Re2d('.', r'#.*#.*#')
flags = MULTIFIND
print 'Found:', reob10h.search(matching, flags), reob10v.search(matching, flags)
print 'Not found:', reob10h.search(nomatch, flags), reob10v.search(nomatch, flags)
Ở đây chúng tôi sử dụng 2 tìm kiếm để tìm trận đấu theo cả hai hướng. Nó có thể tìm thấy nhiều trận đấu trực giao nhưng cách tiếp cận này không hỗ trợ các đường chéo.
12. Avoid qQ
Found: (2, 2, 4, 4)
Not found: None
print '\n12. Avoid qQ'
reob12 = Re2d('[^qQ]{4,4}')
print 'Found:', reob12.search('bhtklkwt\nqlwQklqw\nvtvlwktv\nkQtwkvkl\nvtwlkvQk\nvnvevwvx')
print 'Not found:', reob12.search('zxvcmn\nxcvncn\nmnQxcv\nxcvmnx\nazvmne')
Tìm kiếm này tìm thấy trận đấu đầu tiên.
13. Diamond Mining
.X.
X.X
.X.
.X.
X.X
.X.
..X..
./.\.
X...X
.\./.
\.X..
..X..
./.\.
X...X
.\./.
..X..
.XX.\
//.\.
X...X
.\./.
..X..
...X...
../.\..
./.X.\.
X.X.X.X
.\.X.//
..\./X.
.X.X..\
Diamonds: [(2, 2, 3, 3), (0, 6, 3, 3)] [(8, 0, 5, 5), (10, 2, 5, 5), (5, 3, 5, 5)] [(0, 0, 7, 7)]
Not found: None None None
print '\n13. Diamond Mining'
reob13a = Re2d(r'.X.|X.X', flags=MULTIFIND)
reob13b = Re2d(r'..X..|./.\\.|X...X|.\\./.', flags=MULTIFIND)
reob13c = Re2d(r'...X...|../.\\..|./...\\.|X.....X|.\\.../.|..\\./..', flags=MULTIFIND)
match = '''
...X......X....
../.\..../.\...
./.X.\..X...X..
X.X.X.XX.\./.\.
.\.X.//.\.X...X
..\./X...X.\./.
.X.X..\./...X..
X.X....X.......
.X.............
'''.strip().replace(' ', '')
nomatch = '''
.X......./....
.\....X.......
...X.\.\...X..
..X.\...\.X.\.
...X.X...X.\.X
../X\...\...X.
.X...\.\..X...
..\./.X....X..
...X..../.....
'''.strip().replace(' ', '')
for diamond in reob13a.findall(match)+reob13b.findall(match)+reob13c.findall(match):
print diamond+'\n'
print 'Diamonds:', reob13a.search(match), reob13b.search(match), reob13c.search(match)
print 'Not found:', reob13a.search(nomatch), reob13b.search(nomatch), reob13c.search(nomatch)
Vấn đề kim cương là khó khăn hơn. Ba đối tượng tìm kiếm là cần thiết cho ba kích cỡ. Nó có thể tìm thấy sáu viên kim cương trong bộ thử nghiệm, nhưng nó không mở rộng thành những viên kim cương có kích thước thay đổi. Đây chỉ là một giải pháp một phần cho vấn đề kim cương.
Mã Python 2
import sys
import re
DEBUG = re.DEBUG
IGNORECASE = re.IGNORECASE
LOCALE = re.LOCALE
UNICODE = re.UNICODE
VERBOSE = re.VERBOSE
MULTIFIND = 1<<11
ROTATED = 1<<12 # not implemented
HORFLIP = 1<<13
VERFLIP = 1<<14
WRAPAROUND = 1<<15 # not implemented
class Re2d(object):
def __init__(self, horpattern, verpattern=None, flags=0):
self.horpattern = horpattern
self.verpattern = verpattern if verpattern != None else horpattern
self.flags = flags
def checkblock(self, block, flags):
'Return a position if block matches H and V patterns'
length = []
for y in range(len(block)):
match = re.match(self.horpattern, block[y], flags)
if match:
length.append(len(match.group(0)))
else:
break
if not length:
return None
width = min(length)
height = len(length)
length = []
for x in range(width):
column = ''.join(row[x] for row in block[:height])
match = re.match(self.verpattern, column, flags)
if match:
matchlen = len(match.group(0))
length.append(matchlen)
else:
break
if not length:
return None
height = min(length)
width = len(length)
# if smaller, verify with RECURSIVE checkblock call:
if height != len(block) or width != len(block[0]):
newblock = [row[:width] for row in block[:height]]
newsize = self.checkblock(newblock, flags)
return newsize
return width, height
def mkviews(self, text, flags):
'Return views of text block from flip/rotate flags, inc inverse f()'
# TODO add ROTATED to generate more views
width = len(text[0])
height = len(text)
views = [(text, lambda x,y,w,h: (x,y,w,h))]
if flags & HORFLIP and flags & VERFLIP:
flip2text = [row[::-1] for row in text[::-1]]
flip2func = lambda x,y,w,h: (width-1-x, height-1-y, -w, -h)
views.append( (flip2text, flip2func) )
elif flags & HORFLIP:
hortext = [row[::-1] for row in text]
horfunc = lambda x,y,w,h: (width-1-x, y, -w, h)
views.append( (hortext, horfunc) )
elif flags & VERFLIP:
vertext = text[::-1]
verfunc = lambda x,y,w,h: (x, height-1-y, w, -h)
views.append( (vertext, verfunc) )
return views
def searchview(self, textview, flags=0):
'Return matching textview positions or None'
result = []
for y in range(len(textview)):
testtext = textview[y:]
for x in range(len(testtext[0])):
size = self.checkblock([row[x:] for row in testtext], flags)
if size:
found = (x, y, size[0], size[1])
if flags & MULTIFIND:
result.append(found)
else:
return found
return result if result else None
def search(self, text, flags=0):
'Return matching text positions or None'
flags = self.flags | flags
text = text.split('\n') if type(text) == str else text
result = []
for textview, invview in self.mkviews(text, flags):
found = self.searchview(textview, flags)
if found:
if flags & MULTIFIND:
result.extend(invview(*f) for f in found)
else:
return invview(*found)
return result if result else None
def findall(self, text, flags=0):
'Return matching text blocks or None'
flags = self.flags | flags
strmode = (type(text) == str)
text = text.split('\n') if type(text) == str else text
result = []
positions = self.search(text, flags)
if not positions:
return [] if flags & MULTIFIND else None
if not flags & MULTIFIND:
positions = [positions]
for x0,y0,w,h in positions:
if y0+h >= 0:
lines = text[y0 : y0+h : cmp(h,0)]
else:
lines = text[y0 : : cmp(h,0)]
if x0+w >= 0:
block = [row[x0 : x0+w : cmp(w,0)] for row in lines]
else:
block = [row[x0 : : cmp(w,0)] for row in lines]
result.append(block)
if strmode:
result = ['\n'.join(rows) for rows in result]
if flags & MULTIFIND:
return result
else:
return result[0]
def match(self, text, flags=0):
'Return True if whole text matches the patterns'
flags = self.flags | flags
text = text.split('\n') if type(text) == str else text
for textview, invview in self.mkviews(text, flags):
size = self.checkblock(textview, flags)
if size:
return True
return False