从 Python 中的列表中删除单词时出现问题

Issues removing words from a list in Python

我正在构建一个 Wordle 求解器。基本上从列表中删除单词,如果它们没有特定字符,或者没有它们在特定位置。我还不关心最优选择的统计数据。

当我 运行 下面的代码(我认为所有相关部分都包括在内)时,我的输出很清楚它找到了一个与 'word of the day' 匹配的字母位置。但是在下一次迭代中,它会选择一个没有那个字母的单词,而它应该只从剩余的单词中选择 select。

字真的没有被删除吗?还是有什么东西遮住了我找不到的范围? 我重写了整个部分,发生了完全相同的问题。

#Some imports and reading the word list here. 

def word_compare(word_of_the_day, choice_word):
    results = []
    index = 0
    letters[:] = choice_word
    for letter in letters:
        if letter is word_of_the_day[index]:
            results.append((letter, 2, index))
        elif letter in word_of_the_day:
            results.append((letter, 1, index))
        else:
            results.append((letter, 0, index))
        index += 1
    print("\nIteration %s\nWord of the Day: %s,\nChoice Word: %s,\nResults: %s" % (
        iteration, word_of_the_day, choice_word, results))
    return results


def remove_wrong_words():
    for item in results:
        if item[1] == 0:
            for word in words:
                if item[0] in word:
                    words.remove(word)
    for item in results:
        if item[1] == 2:
            for word in words:
                if word[item[2]] != item[0]:
                    words.remove(word)
    print("Words Remaining: %s" % len(words))
    return words


words, letters = prep([])
# choice_word = best_word_choice()
choice_word = "crane"
iteration = 1
word_of_the_day = random.choice(words)

while True:
    if choice_word == word_of_the_day:
        break
    else:
        words.remove(choice_word)
        results = word_compare(word_of_the_day, choice_word)
        words = remove_wrong_words()
        if len(words) < 10:
            print(words)
        choice_word = random.choice(words)
        iteration += 1

我得到的输出:

Iteration 1
Word of the Day: stake,
Choice Word: crane,
Results: [('c', 0, 0), ('r', 0, 1), ('a', 2, 2), ('n', 0, 3), ('e', 2, 4)]
Words Remaining: 386

Iteration 2
Word of the Day: stake,
Choice Word: lease,
Results: [('l', 0, 0), ('e', 1, 1), ('a', 2, 2), ('s', 1, 3), ('e', 2, 4)]
Words Remaining: 112

Iteration 3
Word of the Day: stake,
Choice Word: paste,
Results: [('p', 0, 0), ('a', 1, 1), ('s', 1, 2), ('t', 1, 3), ('e', 2, 4)]
Words Remaining: 81

Iteration 4
Word of the Day: stake,
Choice Word: spite,

...这会持续一段时间直到解决。在此输出中,发现 'a' 在第二次迭代中位于正确的位置(元组中的值 2)。这应该从列表中删除所有没有 'a' 作为第三个字符的单词。相反,'paste' 和 'spite' 是从同一个列表中选择用于以后的迭代,而不是被删除。

我认为您的问题之一是以下行:if letter is word_of_the_day[index]:。这应该是 == 而不是 is,因为后者检查的是被比较的两个对象是否具有相同的内存地址(即 id()),而不是它们是否具有相同的值。因此,results 永远不会 return 位置 1 值为 2 的元组,所以这意味着 remove_wrong_words 中的第二个 for 循环也不会做任何事情。可能还有更多的事情要发生,但我想要一个具体的例子 运行 在进一步挖掘之前。

您的问题与在迭代时从列表中删除项目有关。这通常会导致跳过后面的值,因为列表迭代是由索引在幕后处理的。

具体来说,问题出在这里(也可能在另一个循环中):

for word in words:
    if item[0] in word:
        words.remove(word)

如果 if 条件对于 words 列表中的第一个单词为真,则不会检查第二个单词。那是因为当 for 循环向列表迭代器询问下一个值时,它将产生列表的第二个值 ,因为它现在是 ,这将是原始列表中的第三个值(因为第一个已经消失)。

有几种方法可以避免此问题。

一种方法是迭代您要修改的列表的副本。这意味着迭代器永远不会跳过任何内容,因为复制的列表在您进行时没有从中删除任何内容(只有原始列表正在更改)。制作副本的常用方法是使用切片:

for word in words[:]:       # iterate on a copy of the list
    if item[0] in word:
        words.remove(word)  # modify the original list here

另一种选择是构建一个包含原始列表中 有效 值的新列表,而不是删除无效值。列表推导通常就足够了:

words = [word for word in words if item[0] not in word]

这在您的示例中可能会稍微复杂一些,因为您使用的是全局变量。您可能需要更改该设计(例如接受列表作为参数和 return 新版本),或者添加 global words 语句让函数的代码重新绑定全局变量(而不是修改它到位)。