忽略字符串中的大小写、标点和空格
如何在处理字符串时高效地忽略大小写、标点和空格呢?这些字符串应该按单词来分,而不是按字符来分。在比较和切片这些单词字符串时,速度要尽可能快。
我原本打算在下面的代码中使用不区分大小写和标点的字符串,但看到评估 class Slice: def __eq__(self, other): return self.root == other.root
需要花费很长时间后,我决定改用 data = tuple(string.split())
。因为让字符串在处理时不区分大小写、标点和空格,并且按单词而不是字符来处理,会让下面的代码变得非常复杂和耗时。
class Slice:
def __init__(self, data, offset, length):
self.prefix = data[:offset]
self.root = data[offset:offset+length]
self.suffix = data[offset+length:]
def __eq__(self, other):
return self.root == other.root
def __len__(self):
return len(self.root)
################################################################################
class Match:
def __init__(self, data, key, prefix_tree, suffix_tree):
self.data = data
self.key = key
self.prefix_tree = prefix_tree
self.suffix_tree = suffix_tree
self.__value = len(key) + prefix_tree.value() + suffix_tree.value()
def value(self):
return self.__value
################################################################################
class Tree(tuple):
def __new__(cls, nodes):
tree = super().__new__(cls, nodes)
tree.__value = max(map(Match.value, tree)) if tree else 0
return tree
def value(self):
return self.__value
def find(self, value):
for index, match in enumerate(self):
if match.value() == value:
return index
raise ValueError()
################################################################################
def search(data, key):
length = 0
nodes = []
for d_block in shrink(data, len(key)):
block_len = len(d_block)
if length > block_len:
return Tree(nodes)
for k_block in slide(key, block_len):
if d_block == k_block:
length = block_len
prefix_tree = search(d_block.prefix, k_block.prefix)
suffix_tree = search(d_block.suffix, k_block.suffix)
match = Match(d_block, k_block, prefix_tree, suffix_tree)
nodes.append(match)
return Tree(nodes)
def shrink(data, max_len):
for length in range(min(len(data), max_len), 0, -1):
for block in slide(data, length):
yield block
def slide(data, length):
for offset in range(len(data) - length + 1):
yield Slice(data, offset, length)
################################################################################
def build_tree(nodes):
match = nodes[nodes.find(nodes.value())]
node = match.key
if match.prefix_tree:
node.prefix = build_tree(match.prefix_tree)
if match.suffix_tree:
node.suffix = build_tree(match.suffix_tree)
return node
def flatten_tree(node):
array = [0]
_flatten(node, array)
return tuple(array)
def _flatten(node, array):
if isinstance(node.prefix, Slice):
_flatten(node.prefix, array)
else:
array.append(node.prefix)
array[0] += 1
array.append((array[0], node.root))
if isinstance(node.suffix, Slice):
_flatten(node.suffix, array)
else:
array.append(node.suffix)
2 个回答
如果你想让一个字符串的实例在迭代时能遍历它的 self.__string
,就像你的 __iter__
方法所表明的那样,那么返回 __string
的长度是唯一合理的选择。如果 len(x)
和 sum(1 for _ in x)
返回的值不一样,那就真的很奇怪了。
我得承认,我不太明白这个类的目的是什么(尤其是你为什么选择使用旧式的方式,以及你为什么用这么复杂的方式来构建 __simple
),但内部的一致性还是很重要的。所以,要么改一下 __iter__
,要么让 __len__
和它逻辑上兼容。
你的切片逻辑我也完全搞不懂——为什么你要以一种可能和从切片的 __string
重建得到的结果不同的方式来构建切片的 __simple
?比如,如果 self.__string
是 '?Boh!',因此 self.__simple
是 'boh',那你为什么会想要 self[1:-1]
的 __string
是 'Boh',但 __simple
是 'o',这样就不兼容、不一样,也和从切片重新计算得到的 __simple
不一致呢……?
我想这和关于长度的问题没有直接关系,但我只是对你做出的这些非常奇怪的设计选择感到好奇……
“解决这个问题的最好方法是什么?”
最好的方法,也是唯一的方法,就是先弄清楚这个对象“代表”什么,以及这个对象的“长度”又代表什么。
这个对象看起来像是一个单词的列表,仅此而已。这似乎就是_string
中的内容。
至于_simple
是什么,除了它是_string
中单词的一个无法访问的过滤子集,其他的就不太清楚了。
那么,长度是什么呢?是单词的长度,还是过滤后子集中的单词长度?
只有你才能定义这个类代表什么。这个定义将决定如何实现__len__
。在你明确这个定义之前,根本无法确定任何东西应该如何实现。