Soundex is an algorithm for creating indices for words based on their pronunciation.



Task

The goal is for homophones to be encoded to the same representation so that they can be matched despite minor differences in spelling   (from the   soundex   Wikipedia article).

Caution

There is a major issue in many of the implementations concerning the separation of two consonants that have the same soundex code! According to the official Rules [[1]]. So check for instance if Ashcraft is coded to A-261.

 If a vowel (A, E, I, O, U) separates two consonants that have the same soundex code, the consonant to the right of the vowel is coded. Tymczak is coded as T-522 (T, 5 for the M, 2 for the C, Z ignored (see "Side-by-Side" rule above), 2 for the K). Since the vowel "A" separates the Z and K, the K is coded.
 If "H" or "W" separate two consonants that have the same soundex code, the consonant to the right of the vowel is not coded. Example: Ashcraft is coded A-261 (A, 2 for the S, C ignored, 6 for the R, 1 for the F). It is not coded A-226.


go =>
   Names = split("Lloyd Woolcock Donnell Baragwanath Williams Ashcroft Ashcraft Euler
Ellery Gauss Ghosh Hilbert Heilbronn Knuth Kant Ladd Lukasiewicz Lissajous O'Hara"),
   SoundexNames = split("L300 W422 D540 B625 W452 A261 A261 E460 
E460 G200 G200 H416 H416 K530 K530 L300 L222 L222 O600"),
 
   foreach({Name,Correct} in zip(Names, SoundexNames)) 
     S = soundex(Name),
     printf("%s: %s ",Name,S),
     if S == Correct then
       println("ok")
     else 
       printf("not correct! Should be: %s\n", Correct)
     end
   end,
   nl.
 
soundex("", _) = "" => true.
soundex(Word) = Soundex =>
  SoundexAlphabet = "0123012#02245501262301#202",
  Soundex = "",
  LastC = '?',  
  foreach(Ch in Word.to_uppercase,
             C = ord(Ch), C >= 0'A', C <= 0'Z',
             Soundex.len < 4)
     ThisC := SoundexAlphabet[C-0'A'+1],
     Skip = false, % to handle '#'
     if Soundex.len == 0 then
       Soundex := Soundex ++ [Ch]
     elseif ThisC == '#' then
       Skip := true
     elseif ThisC != '0', ThisC != LastC then
       Soundex := Soundex ++ [ThisC]
     end,
     if Skip == false then
       LastC := ThisC
     end
  end,
  Soundex := Soundex.padRight(4,'0').
 
padRight(S,Len,PadChar) = S ++ [PadChar : _ in 1..Len-S.len].