Finding Semordnilaps

Written by Ben Wendt

My wife recently developed an interest in semordnilaps, so I thought I would take a stab at writing a script that will find some. What I came up with finds a subset of all two word to two word semordnilaps. Generally you don’t consider whitespace and punctuation in palindromes and semordnilaps, so this code doesn’t either.

It turns out that filtering out proper names vastly reduces the number of semordnilaps found, and I didn’t really appreciate the ones which contained proper names anyway, so I took those out.

words = {}
File.open("/usr/share/dict/words") do |file|
  file.each do |line|
    if line.length > 4 # don't accept one or two letter words.
      word = line.strip
      next if word.downcase != word # proper names have capitals, exclude those.
      words[word.downcase.gsub(/[^a-z]/, '')] = true
    end
  end
end

words.each do |word1, k|  
  p_words = {}
  word_finds = 0

  word1 = word1.reverse
  first_words = []
  # skip words that when reversed don't match the ending of another word.
  (1 .. (word1.length - 1)).each do |i|
    if words[word1[0..i]]
      first_words << word1[0..i]
    end
  end
  if first_words.count == 0
    next
  end

  words.each do |word2, k|  
    two_words = word1 + word2.reverse    
    (2 .. two_words.length).each do |i|
       if words[two_words[0..(i - 1)]] && words[two_words[i..(two_words.length-1)]]
         p_words[two_words[0..(i - 1)] + ' ' + two_words[i..(two_words.length-1)]] = word2 + ' ' + word1.reverse
         word_finds += 1
       end
    end
  end
  if word_finds > 0
    puts p_words
  end
end

And this gives you some great semordnilaps like:

  • cite catnip”=>”pint acetic”
  • “stub aloof”=>”fool abuts”
  • “tubas gals”=>”slags abut”
  • “sane railed”=>”deli arenas”
  • “reis trailed”=>”deli artsier”
  • “diva lived”=>”devil avid”
  • “stabs faced”=>”decafs bats”

All told, I got a 2.7MB text file. I am sure that there are better ones in there.