Palindrome (No Punctuation)

A “Palindrome” is a word or phrase that reads the same forwards as it does backwards. For example, the word “racecar” backwards is still “racecar”. This is a useful concept to practice with because it covers most aspects of “string” (group of characters) manipulation, which can come up often.

This is in the “advanced” section because this implementation will also be ignoring punctuation, spaces, and other non-letter characters. So we want our function to still return true for a string like “Madam, I’m Adam”, which you’ll notice is still a palindrome without the punctuation (“madamimadam”).

Instead of reversing the string like in the simpler implementation ( Palindrome ), this implementation compares a left letter and the opposite letter on the right (e.g. for “straws” ‘s’ and ‘s’, then ‘t’ and ‘w’, etc.), and keeps going until the left index (“spot”) crosses the right index. To ignore punctuation, these indexes are shifted over whenever there is a non-letter character (each language has a method to check for this). We also want to convert letters to lowercase when comparing, which is also done with language-specific methods.

The typical way to do this is by comparing the left character and the right character. If they’re different, return false, but if you get through the whole string (left spot isn’t less than right spot anymore), return true. This can be seen below:

#include <iostream>
#include <string>
using namespace std;

bool palindrome(string word){
  int left = 0;
  int right = word.length() - 1;
  while(left < right){
    if(!isalpha(word[left])){
      left++;
    }else if(!isalpha(word[right])){
      right--;
    }else if(tolower(word[left]) != tolower(word[right])){
      return false;
    }else{
      left++;
      right--;
    }
  }
  return true;
}
int main(){
  cout << boolalpha;
  cout << palindrome("racecar") << endl;
  cout << palindrome("Madam, I'm Adam") << endl;
  cout << palindrome("not palindrome") << endl;
  return 0;
}
class Program{
  static bool palindrome(string word){
    int left = 0;
    int right = word.Length - 1;
    while(left < right){
      if(!char.IsLetter(word[left])){
        left++;
      }else if(!char.IsLetter(word[right])){
        right--;
      }else if(char.ToLower(word[left]) != char.ToLower(word[right])){
        return false;
      }else{
        left++;
        right--;
      }
    }
    return true;
  }
  static void Main(string[] args){
    System.Console.WriteLine(palindrome("racecar"));
    System.Console.WriteLine(palindrome("Madam, I'm Adam"));
    System.Console.WriteLine(palindrome("not palindrome"));
  }
}
public class palindromeAdvanced {
  public static boolean palindrome(String word){
    int left = 0;
    int right = word.length() - 1;
    while (left < right){
      char leftChar = word.charAt(left);
      char rightChar = word.charAt(right);
      if(!Character.isLetter(leftChar)){
        left++;
      }else if(!Character.isLetter(rightChar)){
        right--;
      }else if(Character.toLowerCase(leftChar) != Character.toLowerCase(rightChar)){
        return false;
      }else{
        left++;
        right--;
      }
    }
    return true;
  }
  public static void main(String[] args) {
    System.out.println(palindrome("racecar"));
    System.out.println(palindrome("Madam I'm Adam"));
    System.out.println(palindrome("not palindrome"));
  }
}
function palindrome(word){
  var left = 0;
  var right = word.length - 1;
  while (left < right){
    if (word[left].toLowerCase() === word[left].toUpperCase()){
      left += 1;
    }else if(word[right].toLowerCase() === word[right].toUpperCase()){
      right -= 1;
    }else if(word[right].toLowerCase() !== word[left].toLowerCase()){
      return false;
    }else{
      left += 1;
      right -= 1;
    }
  }
  return true;
}
console.log(palindrome("racecar"));
console.log(palindrome("Madam I'm Adam"));
console.log(palindrome("not palindrome"));
def palindrome(word):
  left = 0
  right = len(word) - 1
  while left < right:
    if not word[left].isalpha():
      left += 1
    elif not word[right].isalpha():
      right -= 1
    elif word[left].lower() != word[right].lower():
      return False
    else:
      left += 1
      right -= 1
  return True

print(palindrome("racecar"))
print(palindrome("Madam I'm Adam"))
print(palindrome("not palindrome"))
func palindrome(_ word: String) -> Bool{
  var left = 0
  var right = word.count-1
  while left < right{
    let leftChar = word[word.index(word.startIndex, offsetBy: left)]
    let rightChar = word[word.index(word.startIndex, offsetBy: right)]
    if !leftChar.isLetter{
      left += 1
    }else if !rightChar.isLetter{
      right -= 1
    }else if leftChar.lowercased() != rightChar.lowercased(){
      return false
    }else{
      left += 1
      right -= 1
    }
  }
  return true
}
print(palindrome("racecar"))
print(palindrome("Madam I'm Adam"))
print(palindrome("not palindrome"))

Notes

Most of this implementation should be self-explanatory by going through it slowly, but here are the language-specific things to keep in mind:

Regardless of compiler, you should use #include <string> so this works on all compilers.

In C++, the functions to check for a letter and change it to lower case are isalpha(character) and tolower(character) respectively, as part of the standard library (std namespace). They return a bool (true or false) type.

An unimportant detail here is the boolalpha flag (option) passed to cout. This tells the compiler to output the words true and false when detecting a boolean, instead of 1 and 0. You should know 1 is true and 0 is false in computing anyways, and if you didn’t, now you know.

Language-specific things:

.Length is a class property, unlike the method in c++ (no parenthesis after).

Letter-checking and case-changing are class methods that take an argument of the class, so you need to use the char class directly. E.g. char.IsLetter(letter) and char.ToLower(letter). There is also IsDigit and IsLetterOrDigit if needed.

Unlike other languages, characters of a string in Java are accessed with a method, with the syntax word.charAt(index).

Similar to C#, letter-checking and case-changing are done with class methods that take the class type, so the syntax for the Character class is Character.isLetter(character) and Character.toLowerCase(character) respectively.

JavaScript doesn’t have a method to check whether a character is a letter or not. Instead, you can use a handy little trick to check instead: compare the lowercase and uppercase versions of the character. If they are different, it is a letter, and if they are the same, it is almost always not a letter (there are exceptions, but those characters are almost never used). There are ways to check for a letter beyond this, but this is easier to use and understand. The syntax is letter.toLowerCase() === letter.toUpperCase().

Syntax to change cases of a letter can be seen in the last line of the above.

Also, instead of using the ++ and -- (increment and decrement) operators, I’ve used the += and -= operators (add from and subtract from) operators in the form number += 1, which does the same thing as number++. These operators are used in almost every language, whereas increment and decrement sometimes get left out. You should be familiar with both.

Length of a string is in the form len(string). Syntaxes for letter-checking and case-changing are letter.isalpha() and letter.lower().

Incrementing and Decrementing in Python by one isn’t a thing (++ and --), so instead += 1 and -= 1 are used, which is the same but more readable. The reasoning behind this is that number++ and ++number used in other languages in different situations can cause some confusion in output, so this is a way of avoiding that.

Strings in swift are less standardized because different standard libraries use different data types for a character (utf8 and utf16, for instance) because sometimes they want to use emojis. Because of that, string operations in swift can be more verbose.

To get a character from a string, for instance, the syntax is not word[index] like other languages, but word[word.index(word.startIndex, offsetBy: index)]. For this reason, many choose to extend the string class to accept the [] operators and act similarly, but we are ignoring that here and using what is official.

Once you have your character, character properties have improved in Swift 5, so the property character.isLetter can be used for letter checking and the member method character.lowercased() can be used for casing.