-3
\$\begingroup\$

I'm new to python3, and I tried to test if my palindrome codes work and pass most of the edge cases. so I created the following 2 python files below. When I ran the python3 test_palindromes.py on the terminal, it shows that 2 tests passed. And I was wondering if that is sufficient for my palindrome function. I think I have exhausted all of the cases.

Here's my palindromes.py

#!python

# Hint: use string.ascii_letters (all letters in ASCII character set)
import string


def is_palindrome(text):
    """A string of characters is a palindrome if it reads the same forwards and
    backwards, ignoring punctuation, whitespace, and letter casing"""
    # implement is_palindrome_iterative and is_palindrome_recursive below, then
    # change this to call your implementation to verify it passes all tests
    assert isinstance(text, str)
    return is_palindrome_iterative(text)
    # return is_palindrome_recursive(text)


def is_palindrome_iterative(text):
    # TODO: implement the is_palindrome function iteratively here
    # once implemented, change is_palindrome to call is_palindrome_iterative
    # to verify that your iterative implementation passes all tests
    """str.isalpha()
    Return true if all characters in the string are alphabetic and there is at
    least one character, false otherwise. Alphabetic characters are those 
    characters defined in the Unicode character database as “Letter”, i.e.,
    those with general category property being one of “Lm”, “Lt”, , “Ll”, or “Lo”.
    Note that this is different from the “Alphabetic” property 
    defined in the Unicode Standard. (edited)"""
    # First, setting up 2 pointer. First and last pointer.

    first_pointer = 0
    last_pointer = len(text) - 1
    # iteration through when the first index is less than the last index
    while(first_pointer <= last_pointer):

    # set up different while loop condition to do comparison
    # test different condition of the palindrome cases
    # 
        # Get letters only
        while not text[first_pointer].isalpha():
            first_pointer += 1
            if first_pointer > len(text) - 1:
                return True
        while not text[last_pointer].isalpha():
            last_pointer -= 1
            if last_pointer < 0:
                return True

        # Not same, return
        if(text[first_pointer].lower() != text[last_pointer].lower()):
            return False

        first_pointer += 1
        last_pointer -= 1

    return True


def main():
    import sys
    args = sys.argv[1:]  # Ignore script file name
    if len(args) > 0:
        for arg in args:
            is_pal = is_palindrome(arg)
            result = 'PASS' if is_pal else 'FAIL'
            str_not = 'a' if is_pal else 'not a'
            print('{}: {} is {} palindrome'.format(result, repr(arg), str_not))
    else:
        print('Usage: {} string1 string2 ... stringN'.format(sys.argv[0]))
        print('  checks if each argument given is a palindrome')


if __name__ == '__main__':
    main()

my test_palindrome.py

import unittest


def test_is_palindrome_with_mixed_casing_and_punctuation(self):
    # palindromes with whitespace, punctuation and mixed letter casing
    assert is_palindrome('No, On!') is True
    assert is_palindrome('Dog God?') is True
    assert is_palindrome('Taco? Cat.') is True
    assert is_palindrome('Race-Car!!!') is True
    assert is_palindrome('Race Fast, Safe Car...') is True
    assert is_palindrome('Was it a car or a cat I saw?') is True
    assert is_palindrome("Go hang a salami, I'm a lasagna hog.") is True
    assert is_palindrome('A man, a plan, a canal - Panama!') is True

def test_is_palindrome_with_non_palindromic_strings(self):
    # examples of non-palindromic strings that should be rejected
    assert is_palindrome('AB') is False  # even length
    assert is_palindrome('ABC') is False  # odd length
    assert is_palindrome('AAB') is False
    assert is_palindrome('AABB') is False
    assert is_palindrome('AAABB') is False
    assert is_palindrome('AAABBB') is False
    assert is_palindrome('ABCZBA') is False
    assert is_palindrome('ABCCZA') is False
    assert is_palindrome('ABCCBZ') is False
    assert is_palindrome('ABCDZCBA') is False
    assert is_palindrome('ABCDDZBA') is False
    assert is_palindrome('ABCDDCZA') is False
    assert is_palindrome('ABCDDCBZ') is False
    assert is_palindrome('AAAAZAAA') is False
    assert is_palindrome('AAAAAAAZ') is False


if __name__ == '__main__':
    unittest.main()

python3 palindromes_test.py

.......

Ran 7 tests in 0.001s

\$\endgroup\$
  • \$\begingroup\$ You unit tests seems off, is it missing a few lines? \$\endgroup\$ – Ludisposed Oct 30 '17 at 8:18
  • \$\begingroup\$ You might want to add tests for '', 'a', '!', '\n', 'a\n' (all yes), 'á là' (yes, but for a sneaky reason), 'a là' (no), and '!race car' (yes; tests initial punctuation). Your implementation successfully passes all these tests. Code-wise, though, @Ludisposed has managed to distill your 36 lines down to 3 lines, so is your code good? no, not if it's overlong by a factor of 12. \$\endgroup\$ – Quuxplusone Oct 30 '17 at 8:52
  • \$\begingroup\$ This question has been mentioned on Meta. \$\endgroup\$ – 200_success Oct 30 '17 at 16:51
3
\$\begingroup\$

PEP8 and readability

  • imports should be on top of the file, so remove import sys from main
  • You have a few great docstrings, there is no need for block-comments and docstrings.
  • In is_palindrome you have this assert isinstance(text, str) but don't use it.

Code changes

Your is_palindrome_iterative could be alot more simplified. Because a normal palindrome is just checking if the word is the same as reversed. Now the same goes for words with punctuation, but we just have to remove them first.

When I apply my thinking, I end up with something like this. It might not be iterative but it looks alot shorter/cleaner.

I first remove punctuation, with a list comprehension in combination with a check if a letter is in string.ascii_letters afterwards join the word together and check if it is the same as reversed.

def is_palindrome(text):
    """returns True if the text is a palindrome, with punctuation omitted"""
    text = ''.join([i for i in text.lower() if i in string.ascii_letters])
    return text == text[::-1]

Now if we want palindromes that are at least 2 letters, we can add a if check beforehand to check for the length.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.