Search code examples
rubypalindromeenumerablelongest-substring

Ruby longest palindrome


I'm trying to solve the longest palindrome problem in Ruby, and I found the answer on stackoverflow:

ANSWER:

Suppose the string has n characters. First see if the entire string is a palindrome. If it is, return the string. Fini! If not, see if either of the two substrings of length n-1 is a palindrome. If one is, return it. If not, examine substrings of length n-2, and so on. As long as the string contains at least one letter, the longest palindrome will be found.

def longest_palindrome(str)
  arr = str.downcase.chars
  str.length.downto(1) do |n|
    ana = arr.each_cons(n).detect { |b| b == b.reverse }
    return ana.join if ana
  end
end

puts longest_palindrome "ilikeracecar"

But I'm having trouble understanding this line:

return ana.join if ana

What does

if ana

mean?

Also why wouldn't this work?

def longest_palindrome(str)
  arr = str.downcase.chars
  str.length.downto(1) do |n|
    ana = arr.each_cons(n).detect { |b| b == b.reverse }
    return ana.join
  end
end

When I run this, it gives me

undefined method `join' for nil:NilClass (NoMethodError)

But I don't understand why ana would be nil when I've detected the first array that meets the condition which would be ["r", "a", "c", "e", "c", "a", "r"] so shouldn't this be in ana?


Solution

  • Each time this code runs:

    ana = arr.each_cons(n).detect { |b| b == b.reverse }
    

    ana gets a new value. detect will either return the first element that is a palindrome or nil. So in the case where n is a length such that there is no palindrome, ana will be nil.

    return ana.join if ana
    

    means "if ana is true (e.g. non-nil), return ana.join". In your modified code, sometimes ana is nil, and you try to join it anyway.

    It might be easier to understand the code if you add some logging:

    def longest_palindrome(str)
      arr = str.downcase.chars
      str.length.downto(1) do |n|
        puts "n = #{n}"
        ana = arr.each_cons(n).detect { |b| b == b.reverse }
        if ana == nil then
          puts "ana is nil"
        else
          puts "ana = #{ana.join}"
        end
        return ana.join if ana
      end
    end
    
    puts longest_palindrome('a racecar')
    

    Output:

    n = 9
    ana is nil
    n = 8
    ana is nil
    n = 7
    ana = racecar
    racecar
    

    As you can see, ana is nil until you get down to size 7.