4

我只是为了好玩而学习 Python 编程。我正在编写一个回文程序,我想到了如何进一步改进它。

我想到的第一件事是防止程序不得不双向遍历整个单词,因为我们只是在检查回文。然后我意识到只要第一个和最后一个字符不匹配,循环就可以被打破。

然后我在一个类中实现了它们,所以我可以只调用一个词并返回真或假。

这是该程序目前的状态:

class my_str(str):
        def is_palindrome(self):
                a_string = self.lower()
                length = len(self)
                for i in range(length/2):
                        if a_string[i] != a_string[-(i+1)]:
                                return False
                return True

this = my_str(raw_input("Enter a string: "))
print this.is_palindrome()

是否有任何其他改进可以提高效率?

4

6 回答 6

9

我认为在 Python 中即兴编写回文检查函数的最佳方法如下:

def is_palindrome(s):
   return s == s[::-1]

(根据需要添加lower()呼叫。)

于 2012-06-07T12:49:50.480 回答
3

更简单的东西呢?你为什么要创建一个类而不是一个简单的方法?

>>> is_palindrome = lambda x: x.lower() == x.lower()[::-1]
>>> is_palindrome("ciao")
False
>>> is_palindrome("otto")
True
于 2012-06-07T12:49:54.350 回答
3

虽然已经给出了其他答案,讨论了如何最好地解决 Python 中的回文问题,但让我们看看您在做什么。

您正在使用索引遍历字符串。虽然这可行,但它不是很 Pythonic。Pythonfor循环旨在循环您想要的对象,而不是像其他语言那样简单地循环数字。这使您可以减少一层间接性并使您的代码更清晰、更简单。

那么,在您的情况下,我们如何做到这一点?好吧,你想要做的是在一个方向和另一个方向上循环字符 - 同时。我们可以使用zip()和内置函数很好地做到这一点reversed()reversed()允许我们以相反的方向获取字符,同时zip()允许我们一次迭代两个迭代器。

>>> a_string = "something"
>>> for first, second in zip(a_string, reversed(a_string)):
...     print(first, second)
... 
s g
o n
m i
e h
t t
h e
i m
n o
g s

这是同时在两个方向上循环字符的更好方法。当然,这不是解决这个问题的最有效方法,但它是一个很好的例子,说明你应该如何在 Python 中以不同的方式处理事情。

于 2012-06-07T12:53:07.213 回答
2

建立在 Lattyware 的答案之上——通过适当地使用 Python 内置函数,您可以避免做类似 的事情a_string[-(i+1)],这需要一秒钟的时间来理解——而且,在编写比回文更复杂的事情时,很容易出现错误。

诀窍是告诉 Python 你要做什么,而不是如何实现它 - 因此,根据另一个答案,最明显的方法是执行以下操作之一:

s == s[::-1]
list(s) == list(reversed(s))
s == ''.join(reversed(s))

或其他各种类似的东西。他们都说:“字符串是否等于向后的字符串?”。

如果由于某种原因你确实需要优化,你知道你有一个回文一旦你做了一半(你通常不应该,但也许你正在处理极长的字符串),你仍然可以做得比 index算术。你可以从:

halfway = len(s) // 2

(其中//强制结果为整数,即使在 Py3 中或如果你已经完成from __future__ import division)。这将导致:

s[:halfway] == ''.join(reversed(s[halfway:]))

这将适用于所有 even-length s,但对于奇数长度无效,s因为 RHS 将是一个更长的元素。但是,你不关心其中的最后一个字符,因为它是字符串的中间字符——这不会影响它的回文性。如果您zip将两者放在一起,它将在简短的结束后停止-您可以像在原始循环中一样一次比较一个字符:

for f,b in zip(s[:half], reversed(s[half:])):
    if f != b: 
        return False
return True

你甚至不需要''.joinlist这样的。但是你仍然可以做得更好——这种循环是如此地道,以至于 Python 有一个内置函数all来为你做这件事:

all(f == b for f,b in zip(s[:half], reversed(s[half:])))

Says 'all the characters in the front half of the list are the same as the ones in the back half of the list written backwards'.

于 2012-06-07T13:23:12.213 回答
1

我可以看到的一项改进是使用 xrange 而不是 range。

于 2012-06-07T12:50:32.900 回答
0

It probably isn't a faster implementation but you could use a recursive test. Since you're learning, this construction is very useful in many situation :

def is_palindrome(word): 
    if len(word) < 2:
        return True 
    if word[0] != word[-1]:
        return False 
    return is_palindrome(word[1:-1])

Since this is a rather simple (light) function this construction might not be the fastest because of the overhead of calling the function multiple times, but in other cases where the computation are more intensive it can be a very effective construction.

Just my two cents.

于 2012-06-07T15:12:08.590 回答