Python 从字符串中获取非重复字符的递归函数

Python 从字符串中获取非重复字符的递归函数,python,string,list,recursion,Python,String,List,Recursion,我有以下练习: 编写一个递归函数,该函数接受一个字符串并返回该字符串中未重复的所有字符 输出中的字符不需要与输入字符串中的字符具有相同的顺序 首先,我尝试了这个方法,但给定函数停止的条件,它从不计算最后一个字符: i=0 lst = [] def list_of_letters_rec(str=""): if str[i] not in lst and i < len(str) - 1: lst.append(str[i]) l

我有以下练习:

编写一个递归函数,该函数接受一个字符串并返回该字符串中未重复的所有字符

输出中的字符不需要与输入字符串中的字符具有相同的顺序

首先,我尝试了这个方法,但给定函数停止的条件,它从不计算最后一个字符:

i=0
lst = []

def list_of_letters_rec(str=""):
    if str[i] not in lst and i < len(str) - 1:
        lst.append(str[i])
        list_of_letters_rec(str[i+1:])
    elif str[i] in lst and i < len(str) - 1:
        list_of_letters_rec(str[i+1:])
    elif i > len(str) - 1:
        return lst
    
    return lst

word = input(str("Word?"))

print(list_of_letters_rec(word))
显然,停止条件没有很好地定义,特别是在最后一个条件中,因为我得到的输出是

索引器错误:字符串索引超出范围

在第一次或第二次尝试中,您能给我一些提示来帮助我纠正停止条件吗?

您可以尝试:

word = input("> ")
result = [l for l in word if word.count(l) < 2]
一些问题:

由于条件句中的ilenstr-1,否则将得到无效的索引引用。这也使得长度上的其他条件变得不必要。 不要给变量命名为str,因为它已经是字符串类型的常用名称。 不要填充全局列表。通过这样做,您只能可靠地调用函数一次。下一次列表仍将包含上一次调用的结果,您将添加到其中。而是使用从递归调用中获得的列表。在基本情况下,返回一个空列表。 全球我没有用,因为你永远不会改变它的价值;它总是0。因此,您应该只引用索引[0]并检查字符串是否为空。 以下是您的代码及其更正:

def list_of_letters_rec(s=""):
    if not s:
        return []
    result = list_of_letters_rec(s[1:])
    if s[0] not in result:
        result.append(s[0])
    return result

print(list_of_letters_rec("aardvark"))

注意:这不是最好的方法。但我想这就是要求您做的。

一个可能的解决方案是只使用索引而不是拼接字符串:

def list_of_letters_rec(string="", index = 0, lst = []):
    if(len(string) == index):
        return lst
    char = string[index]
    if  string.count(char) == 1:
        lst.append(char)
    return list_of_letters_rec(string, index+1, lst)
word = input(str("Word?"))
print(list_of_letters_rec(word))

对于@trincot的答案,我可以提供的一个改进是使用set,与列表相比,set具有更好的查找时间O1

如果输入字符串s为空,则返回空结果 归纳s至少有一个字符。如果第一个字符s[0]在备忘录mem中,则该字符已被看到。返回子问题的结果,s[1:] 第一个字符不在备忘录中。将第一个字符添加到备忘录中,并将第一个字符添加到子问题的结果s[1:] def列表字母,mem=集合: 如果不是,则: 返回1 内存中的elif s[0]: 返回列表,共有字母[1],成员2 其他: 返回s[0]+字母列表s[1:,{*mem,s[0]}3 lettersaardvark的打印列表 ardvk 根据您的评论,该练习只要求输入一个字符串。我们可以很容易地修改我们的程序,使mem私有化-

def列表字母:公共api def循环,mem:私有api 如果不是,则: 回来 内存中的elif s[0]: 返回循环[1:],mem 其他: 返回s[0]+循环[1:],{*mem,s[0]} 返回循环,设置运行私有函数 调用方看不到字母saardvark mem的打印列表 ardvk Python的原生set数据类型接受一个iterable,它立即解决了这个问题。然而,这并没有教你任何关于递归的东西:D

print.joinsetaardvark akdrv
您的代码引用了未定义的变量lst和lista…@trincot谢谢!我不是以英语为母语的人,所以代码有点“改编”了。已更正该位。您不应为变量指定与常用内置函数相同的名称,即s不应调用字符串str。您正在使用的逻辑中存在错误,但在第二个版本中获取超出范围的输入的原因是,您在检查n>=0之前调用了str[n]。如果只交换这两条语句,程序将继续运行,尽管输出仍然不正确。在这种情况下,为什么要引用函数外部的变量,即lst。使函数过于复杂,通常形式不好。注意OP约束:有一个练习要求我做一个递归函数。这只是一个例子,OP可以将其转换为递归函数。@PedroLobito-我没有否决投票。我认为你的回答是非递归解决方案的一个很好的例子。@Pedrolobi非常感谢你的回答!即使不是递归的,这也是解决问题的一个非常简单的方法,它肯定会帮助我。顺便说一句,我也没有投反对票,因为我甚至没有足够的代表能够做到这一点。很好的回答和建议。@trincot非常感谢你!!:我特别感谢关于正确定义停止条件和变量的建议。我最终设法使我的代码工作,但是使用了嵌套的ifs,因此您的代码更简单/更好。谢谢您的回答!我认为这个练习意味着只有一个输入,字符串,但是看到另一种方法真的很有趣,它也帮助我们学到了更多虽然我很瘦
k最初的练习要求只使用字符串变量作为输入/输出,这是一个非常受欢迎的输入/改进建议。也解释得很清楚。非常感谢你,谢谢你!!我很乐意帮忙。我做了一个编辑,显示了如何使mem对调用方不可见
def list_of_letters_rec(s=""):
    if not s:
        return []
    result = list_of_letters_rec(s[1:])
    if s[0] not in result:
        result.append(s[0])
    return result

print(list_of_letters_rec("aardvark"))
def list_of_letters_rec(string="", index = 0, lst = []):
    if(len(string) == index):
        return lst
    char = string[index]
    if  string.count(char) == 1:
        lst.append(char)
    return list_of_letters_rec(string, index+1, lst)
word = input(str("Word?"))
print(list_of_letters_rec(word))