When I saw the latest in the Lord of the Rings trilogy of movies a short while ago, I wondered how Tolkien had invented the artificial languages of Middle Earth. In my previous article, I told of my desire to discover which real language had been the biggest influence on Tolkien for his invented ones. As a software developer, I wanted to discover this information algorithmically. My idea was to use my own string similarity algorithm to compare each word from a list of Tolkien words to words from 14 other real languages. For each Tolkien word, I would find and record the language with the word that is (lexically) most similar. The set of most-similar words and the languages from which they came would provide new insights into the influences on Tolkien.
Lord Of The Strings Part 2 - A Class Called Compare (Page 4 of 7 )
Now I return to the main logic of the program, which is implemented in five methods of a class called 'Compare'. The top-level structure of the class is as follows (and you can see the full program listing here):
public
class Compare { protected QueryRunner queryRunner;
public Compare() {} protected Word findBestMatch(Word baseWord, Collection collection) {} protected void storeBestMatch(Word w, Word bestMatch, double bestScore) {} protected Word getWord(String lang) throws SQLException {} protected Collection getCandidateStrings(String str) throws SQLException {} }
As you can see, the class uses an instance of a QueryRunner to access the database, and also makes use of the Word class, both as a method parameter and a result. The following pseudo-code explains the algorithm and the interaction between the methods:
OpenDatabaseConnection
; // Get a Tolkien word that has not been considered While (w = getWord("Tolkien") { // Run a first pass query to return promising strings Collection candidates = getCandidateStrings(w); // Find the best match of those candidates findBestMatch(w, candidates); } CloseDatabaseConnection;
First, I open a database connection, and then repeatedly retrieve Tolkien words that have not yet been considered. (The getWord() method runs an SQL query, which returns a Tolkien word that does not yet appear in the comparison results table.) For each Tolkien word, I run a 'first pass' query to retrieve a subset of promising 'candidate' words from all those in the database. I used the database's pattern matching ability to return all those (non-Tolkien) words that start with the same two letters as the Tolkien word. The program then calls findBestMatch() on the Tolkien word and the candidate words to find and store the best matching word. When this has been done for all the Tolkien words, the database connection is closed.
findBestMatch
(Word w, Collection candidates): bestScore = -1; for each c in candidates { similarity = compareStrings(c, w); if (similarity > bestScore) { bestScore = similarity; bestMatch = c; } } storeBestMatch(w, bestMatch, bestScore);
Candidate Strings
In the findBestMatch() method, the program iterates through each candidate word and computes the similarity metric. If the similarity metric is better than the previous best, then the program stores the new value as its current best. Once all the candidate strings have been considered, the best match is saved to the database using an SQL update query.