我有一个填充了字典中单词的列表。我想找到一种方法来删除所有单词,只考虑在目标单词的开头处形成的根单词。
例如,单词“rodeo”将从列表中删除,因为它包含英语有效单词“rode”。 “打字机”将被删除,因为它包含英文有效的单词“type”。但是,单词“snicker”仍然有效,即使它包含单词“nick”,因为“nick”位于单词的中间而不是单词的开头。
我在想这样的事情:
for line in wordlist:
if line.find(...) --
但是我希望“if”语句然后遍历列表中的每个单词检查以查看它是否已找到,如果是,则将其自身从列表中删除,以便只保留根词。我是否必须创建wordlist的副本才能遍历?
答案 0 :(得分:6)
因此,您有两个列表:要检查和可能删除的单词列表,以及有效单词列表。如果您愿意,可以将相同的列表用于这两个目的,但我假设您有两个列表。
对于速度,您应该将有效单词列表转换为集合。然后,您可以非常快速地检查该集合中是否有任何特定单词。然后,取出每个单词,并检查它的所有前缀是否都存在于有效单词列表中。由于“a”和“I”是英语中的有效单词,您是否会删除以“a”开头的所有有效单词,或者您是否有规则设置前缀的最小长度?
我正在使用Ubuntu安装中的文件/ usr / share / dict / words。这个文件中有各种奇怪的东西;例如,它似乎包含每个字母本身作为一个单词。因此,“k”在那里,“q”,“z”等等。据我所知,这些都不是单词,但出于某些技术原因它们可能在那里。无论如何,我决定只从我的有效单词列表中排除短于三个字母的任何内容。
以下是我提出的建议:
# build valid list from /usr/dict/share/words
wfile = "/usr/dict/share/words"
valid = set(line.strip() for line in open(wfile) if len(line) >= 3)
lst = ["ark", "booze", "kite", "live", "rodeo"]
def subwords(word):
for i in range(len(word) - 1, 0, -1):
w = word[:i]
yield w
newlst = []
for word in lst:
# uncomment these for debugging to make sure it works
# print "subwords", [w for w in subwords(word)]
# print "valid subwords", [w for w in subwords(word) if w in valid]
if not any(w in valid for w in subwords(word)):
newlst.append(word)
print(newlst)
如果你是单行的粉丝,你可以取消for列表并使用列表理解:
newlst = [word for word in lst if not any(w in valid for w in subwords(word))]
我认为这比它应该更简洁,我希望能够输入print语句进行调试。
嗯,来想一想,如果你只是添加另一个功能,那就太简洁了:
def keep(word):
return not any(w in valid for w in subwords(word))
newlst = [word for word in lst if keep(word)]
如果你创建这样的函数,Python可以很容易阅读和理解,并给它们起好名字。
答案 1 :(得分:5)
我假设您只有一个列表,您要从中删除在同一列表中包含前缀的任何元素。
#Important assumption here... wordlist is sorted
base=wordlist[0] #consider the first word in the list
for word in wordlist: #loop through the entire list checking if
if not word.startswith(base): # the word we're considering starts with the base
print base #If not... we have a new base, print the current
base=word # one and move to this new one
#else word starts with base
#don't output word, and go on to the next item in the list
print base #finish by printing the last base
编辑:添加了一些注释以使逻辑更明显
答案 2 :(得分:1)
我发现jkerian不是最好的(假设只有一个列表),我想解释原因。
这是我的代码版本(作为函数):
wordlist = ["a","arc","arcane","apple","car","carpenter","cat","zebra"];
def root_words(wordlist):
result = []
base = wordlist[0]
for word in wordlist:
if not word.startswith(base):
result.append(base)
base=word
result.append(base)
return result;
print root_words(wordlist);
只要对单词列表进行排序(如果您愿意,可以在函数中执行此操作),这将在单个解析中获得结果。这是因为当您对列表进行排序时,由列表中的另一个单词组成的所有单词将直接在该根单词之后。例如任何落在你的特定列表中“arc”和“arcane”之间的东西,也会因为根词“arc”而被删除。
答案 3 :(得分:1)
您应该使用内置的lambda
功能。我认为它会让你的生活更轻松
words = ['rode', 'nick'] # this is the list of all the words that you have.
# I'm using 'rode' and 'nick' as they're in your example
listOfWordsToTry = ['rodeo', 'snicker']
def validate(w):
for word in words:
if w.startswith(word):
return False
return True
wordsThatDontStartWithValidEnglishWords = \
filter(lambda x : validate(x), listOfWordsToTry)
这应该适用于您的目的,除非我误解了您的问题。
希望这有帮助
答案 4 :(得分:1)
我写了一个答案,假设有两个列表,要修剪的列表和有效单词列表。在围绕我的回答的讨论中,我评论说也许一个特里解决方案会很好。
到底是什么,我继续写下来。
你可以在这里阅读一下特里:
http://en.wikipedia.org/wiki/Trie
对于我的Python解决方案,我基本上使用了词典。密钥是一系列符号,每个符号进入一个字典,另一个Trie实例作为数据。第二个字典存储“终端”符号,其标记Trie中“单词”的结尾。对于这个例子,“单词”实际上是单词,但原则上单词可以是任何可散列的Python对象序列。
Wikipedia示例显示了一个trie,其中键是字母,但可以是多个字母;它们可以是多个字母的序列。为简单起见,我的代码一次只使用一个符号作为键。
如果你将单词“cat”和单词“catch”都添加到trie中,那么将会有'c','a'和't'的节点(以及第二个'c'in“抓住”)。在'a'的节点级别,“终端”的字典将在其中具有't'(从而完成对“cat”的编码),并且同样在第二'c'的更深节点级别处的终端字典将会有'h'(完成“捕获”)。因此,在“cat”之后添加“catch”只意味着在终端字典中增加一个节点和一个条目。 trie结构是一种非常有效的方法来存储和索引一个非常大的单词列表。
def _pad(n):
return " " * n
class Trie(object):
def __init__(self):
self.t = {} # dict mapping symbols to sub-tries
self.w = {} # dict listing terminal symbols at this level
def add(self, word):
if 0 == len(word):
return
cur = self
for ch in word[:-1]: # add all symbols but terminal
if ch not in cur.t:
cur.t[ch] = Trie()
cur = cur.t[ch]
ch = word[-1]
cur.w[ch] = True # add terminal
def prefix_match(self, word):
if 0 == len(word):
return False
cur = self
for ch in word[:-1]: # check all symbols but last one
# If you check the last one, you are not checking a prefix,
# you are checking whether the whole word is in the trie.
if ch in cur.w:
return True
if ch not in cur.t:
return False
cur = cur.t[ch] # walk down the trie to next level
return False
def debug_str(self, nest, s=None):
"print trie in a convenient nested format"
lst = []
s_term = "".join(ch for ch in self.w)
if 0 == nest:
lst.append(object.__str__(self))
lst.append("--top--: " + s_term)
else:
tup = (_pad(nest), s, s_term)
lst.append("%s%s: %s" % tup)
for ch, d in self.t.items():
lst.append(d.debug_str(nest+1, ch))
return "\n".join(lst)
def __str__(self):
return self.debug_str(0)
t = Trie()
# Build valid list from /usr/dict/share/words, which has every letter of
# the alphabet as words! Only take 2-letter words and longer.
wfile = "/usr/share/dict/words"
for line in open(wfile):
word = line.strip()
if len(word) >= 2:
t.add(word)
# add valid 1-letter English words
t.add("a")
t.add("I")
lst = ["ark", "booze", "kite", "live", "rodeo"]
# "ark" starts with "a"
# "booze" starts with "boo"
# "kite" starts with "kit"
# "live" is good: "l", "li", "liv" are not words
# "rodeo" starts with "rode"
newlst = [w for w in lst if not t.prefix_match(w)]
print(newlst) # prints: ['live']
答案 5 :(得分:0)
我不想提供精确的解决方案,但我认为Python中有两个关键功能可以帮助您。
第一个,jkerian提到:string.startswith()http://docs.python.org/library/stdtypes.html#str.startswith
第二种:过滤器()http://docs.python.org/library/functions.html#filter
使用过滤器,您可以编写一个条件函数,该函数将检查单词是否是另一个单词的基础,如果是,则返回true。
对于列表中的每个单词,您需要迭代所有其他单词并评估条件使用过滤器,它可以返回根词的正确子集。
答案 6 :(得分:0)
我只有一个列表 - 我想从中删除任何另一个词的前缀。
这是一个应该在O(n log N)时间和O(M)空间中运行的解决方案,其中M是返回列表的大小。运行时由排序控制。
l = sorted(your_list)
removed_prefixes = [l[g] for g in range(0, len(l)-1) if not l[g+1].startswith(l[g])] + l[-1:]
如果列表已排序,则索引N处的项目如果在索引N + 1处开始项目则为前缀。
最后,它会附加原始排序列表的最后一项,因为根据定义,它不是前缀。 最后处理它还允许我们迭代超出范围的任意数量的索引。
如果您将禁止列表硬编码在另一个列表中:
banned = tuple(banned_prefixes]
removed_prefixes = [ i for i in your_list if not i.startswith(banned)]
这依赖于startswith接受元组的事实。它可能在接近N * M的地方运行,其中N是列表中的元素,M是banned
中的元素。可以想象,Python可以做一些聪明的事情来让它更快一些。如果您喜欢OP并想忽略大小写,那么您需要在某些地方进行.lower()
次呼叫。