过滤器返回并分配给变量的Iterable似乎没有更新

时间:2019-07-15 07:07:22

标签: python

每日编码问题11中的问题,转载如下:

实施自动完成系统。也就是说,给定一个查询字符串s和一组所有可能的查询字符串,请返回该集中所有以s为前缀的字符串。

例如,给定查询字符串de和字符串集[dog,deer,deal],返回[deer,deal]。

提示:尝试将字典预处理为更有效的数据结构,以加快查询速度。

我想出了一个可行的(希望)解决方案,但是在这样做的过程中,我遇到了一些我无法理解的东西。问题如下。

def autocomplete(word):

    words = []
    ## Set up word import wordDict
    with open('11_word_list.txt','r') as f:
        for line in f:
            words += line.split()

    ## Optional: Filter out words with same first alphabet of search word
    ## Then ensure that remaining words also are at least as long as search word
    words = filter(lambda x: x[0] == word[0], words)
    words = filter(lambda x: len(x) >= len(word), words)

    ## Strictly speaking, this is the only required line that can still
    ## make this solution work
    words = filter(lambda x: x.startswith(word), words)


####################
## Works in progress
####################

##Suppose that the content of words[] are already as long as, or longer than
##the search term, how come only Option B seems to work, but when shortened into
##Option A as a more generic form, it does not work?
##
##Put simply, it seems words[] is not updated after every run of the for loop??

## Option A

##    for i in range(1,len(word)):
##        words = filter(lambda x: x[i] == word[i], words)

## Option B

##    words = filter(lambda x: x[1] == word[1], words)
##    words = filter(lambda x: x[2] == word[2], words)
##    words = filter(lambda x: x[3] == word[3], words)

    return list(words)

在注释区中提到的问题。 选项B最初是为了测试该概念而编写的,尽管它是硬编码的,但它仍然有效。选项A是对选项B进行概括的一种尝试,但是,尽管变量变量在最前面,但变量word似乎并没有得到更新,并且它一直在读取原始单词数组。

1 个答案:

答案 0 :(得分:0)

有两个问题共同导致该问题。修复任何一个问题都可以防止更大的问题。

第一个问题是filter是一个惰性迭代器。它实际上并不会立即处理其输入。当您在其输出迭代器上进行迭代时,它只会跳过错误的值。如果您可以尽早退出迭代,那是一件好事,但是在这里有问题。您可以通过使用非惰性方法进行过滤来避免问题,例如列表理解(或者您可以进行list(filter(...))):

for i in range(1,len(word)):
    words = [x for x in words if x[i] == word[i]]

懒惰的filter有问题的原因是我们的第二个问题。这是因为您的lambda函数是闭包。他们正在从封闭的名称空间而不是从自己的名称空间读取i变量。不幸的是,由于i会随着循环的进行而不断在外部命名空间中变化,因此它们最终都会看到最后一个i值,而不是定义时的值i。有一个解决方法,就是使用i作为lambda中的参数,并将其当前值(在外部名称空间中)作为默认值:

for i in range(1,len(word)):
    words = filter(lambda x, i=i: x[i] == word[i], words)

请注意,这些修复都不会导致非常有效的前缀匹配。对于真正有效的搜索,您可能只想加载一次数据文件,并构建一个可以有效搜索前缀的数据结构。默认情况下,trie会执行此操作,但是如果您不想自己构建(或使用库),则如果您没有太多单词,则从前缀到完整字符串的基本字典映射可能是合理的。