June 2018 Update

This commit is contained in:
Ingy döt Net 2018-06-22 20:57:24 +00:00
parent ba8067c3b7
commit 22f33d4004
5278 changed files with 84726 additions and 14379 deletions

View file

@ -2,10 +2,10 @@ Soundex is an algorithm for creating indices for words based on their pronunciat
;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 [[wp:soundex|the WP article]]).
<br><br>
The goal is for homophones to be encoded to the same representation so that they can be matched despite minor differences in spelling &nbsp; (from the &nbsp; [[wp:soundex|soundex &nbsp; 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 [[https://www.archives.gov/research/census/soundex.html]]. 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.
<br><br>

View file

@ -0,0 +1,77 @@
#include <iostream> // required for debug code in main() only
#include <iomanip> // required for debug code in main() only
#include <string>
std::string soundex( char const* s )
{
static char const code[] = { 0, -1, 1, 2, 3, -1, 1, 2, 0, -1, 2, 2, 4, 5, 5, -1, 1, 2, 6, 2, 3, -1, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0 };
if( !s || !*s )
return std::string();
std::string out( "0000" );
out[0] = (*s >= 'a' && *s <= 'z') ? *s - ('a' - 'A') : *s;
++s;
char prev = code[out[0] & 0x1F]; // first letter, though not coded, can still affect next letter: Pfister
for( unsigned i = 1; *s && i < 4; ++s )
{
if( (*s & 0xC0) != 0x40 ) // process only letters in range [0x40 - 0x7F]
continue;
auto const c = code[*s & 0x1F];
if( c == prev )
continue;
if( c == -1 )
prev = 0; // vowel as separator
else if( c )
{
out[i] = c + '0';
++i;
prev = c;
}
}
return out;
}
int main()
{
static char const * const names[][2] =
{
{"Ashcraft", "A261"},
{"Burroughs", "B620"},
{"Burrows", "B620"},
{"Ekzampul", "E251"},
{"Ellery", "E460"},
{"Euler", "E460"},
{"Example", "E251"},
{"Gauss", "G200"},
{"Ghosh", "G200"},
{"Gutierrez", "G362"},
{"Heilbronn", "H416"},
{"Hilbert", "H416"},
{"Jackson", "J250"},
{"Kant", "K530"},
{"Knuth", "K530"},
{"Ladd", "L300"},
{"Lee", "L000"},
{"Lissajous", "L222"},
{"Lloyd", "L300"},
{"Lukasiewicz", "L222"},
{"O'Hara", "O600"},
{"Pfister", "P236"},
{"Soundex", "S532"},
{"Sownteks", "S532"},
{"Tymczak", "T522"},
{"VanDeusen", "V532"},
{"Washington", "W252"},
{"Wheaton", "W350"}
};
for( auto const& name : names )
{
auto const sdx = soundex( name[0] );
std::cout << std::left << std::setw( 16 ) << name[0] << std::setw( 8 ) << sdx << (sdx == name[1] ? " ok" : " ERROR") << std::endl;
}
return 0;
}

View file

@ -0,0 +1,50 @@
using Soundex
@assert soundex("Ashcroft") == "A261" # true
# Too trivial? OK. Here is an example not using a package:
function soundex(s)
char2num = Dict('B'=>1,'F'=>1,'P'=>1,'V'=>1,'C'=>2,'G'=>2,'J'=>2,'K'=>2,
'Q'=>2,'S'=>2,'X'=>2,'Z'=>2,'D'=>3,'T'=>3,'L'=>4,'M'=>5,'N'=>5,'R'=>6)
s = replace(s, r"[^a-zA-Z]", "")
if s == ""
return ""
end
ret = "$(uppercase(s[1]))"
hadvowel = false
lastletternum = haskey(char2num, ret[1]) ? char2num[ret[1]] : ""
for c in s[2:end]
c = uppercase(c)
if haskey(char2num, c)
letternum = char2num[c]
if letternum != lastletternum || hadvowel
ret = "$ret$letternum"
lastletternum = letternum
hadvowel = false
end
elseif c in ('A', 'E', 'I', 'O', 'U', 'Y')
hadvowel = true
end
end
while length(ret) < 4
ret *= "0"
end
ret[1:4]
end
@assert soundex("Ascroft") == "A261"
@assert soundex("Euler") == "E460"
@assert soundex("Gausss") == "G200"
@assert soundex("Hilbert") == "H416"
@assert soundex("Knuth") == "K530"
@assert soundex("Lloyd") == "L300"
@assert soundex("Lukasiewicz") == "L222"
@assert soundex("Ellery") == "E460"
@assert soundex("Ghosh") == "G200"
@assert soundex("Heilbronn") == "H416"
@assert soundex("Kant") == "K530"
@assert soundex("Ladd") == "L300"
@assert soundex("Lissajous") == "L222"
@assert soundex("Wheaton") == "W350"
@assert soundex("Ashcraft") == "A261"
@assert soundex("Burroughs") == "B620"
@assert soundex("Burrows") == "B620"
@assert soundex("O'Hara") == "O600"

View file

@ -0,0 +1,42 @@
local d, digits, alpha = '01230120022455012623010202', {}, ('A'):byte()
d:gsub(".",function(c)
digits[string.char(alpha)] = c
alpha = alpha + 1
end)
function soundex(w)
local res = {}
for c in w:upper():gmatch'.'do
local d = digits[c]
if d then
if #res==0 then
res[1] = c
elseif #res==1 or d~= res[#res] then
res[1+#res] = d
end
end
end
if #res == 0 then
return '0000'
else
res = table.concat(res):gsub("0",'')
return (res .. '0000'):sub(1,4)
end
end
-- tests
local tests = {
{"", "0000"}, {"12346", "0000"},
{"he", "H000"}, {"soundex", "S532"},
{"example", "E251"}, {"ciondecks", "C532"},
{"ekzampul", "E251"}, {"résumé", "R250"},
{"Robert", "R163"}, {"Rupert", "R163"},
{"Rubin", "R150"}, {"Ashcraft", "A226"},
{"Ashcroft", "A226"}
}
for i=1,#tests do
local itm = tests[i]
assert( soundex(itm[1])==itm[2] )
end
print"all tests ok"

View file

@ -0,0 +1,103 @@
class Soundex
method get_soundex(in_) static
in = in_.upper()
old_alphabet= 'AEIOUYHWBFPVCGJKQSXZDTLMNR'
new_alphabet= '@@@@@@**111122222222334556'
word=''
loop i=1 for in.length()
tmp_=in.substr(i, 1) /*obtain a character from word*/
if tmp_.datatype('M') then word=word||tmp_
end
value=word.strip.left(1) /*1st character is left alone.*/
word=word.translate(new_alphabet, old_alphabet) /*define the current word. */
prev=value.translate(new_alphabet, old_alphabet) /* " " previous " */
loop j=2 to word.length() /*process remainder of word. */
q=word.substr(j, 1)
if q\==prev & q.datatype('W') then do
value=value || q; prev=q
end
else if q=='@' then prev=q
end /*j*/
return value.left(4,0) /*padded value with zeroes. */
method main(args=String[]) static
test=''; result_=''
test['1']= "12346" ; result_['1']= '0000'
test['4']= "4-H" ; result_['4']= 'H000'
test['11']= "Ashcraft" ; result_['11']= 'A261'
test['12']= "Ashcroft" ; result_['12']= 'A261'
test['18']= "auerbach" ; result_['18']= 'A612'
test['20']= "Baragwanath" ; result_['20']= 'B625'
test['22']= "bar" ; result_['22']= 'B600'
test['23']= "barre" ; result_['23']= 'B600'
test['20']= "Baragwanath" ; result_['20']= 'B625'
test['28']= "Burroughs" ; result_['28']= 'B620'
test['29']= "Burrows" ; result_['29']= 'B620'
test['30']= "C.I.A." ; result_['30']= 'C000'
test['37']= "coöp" ; result_['37']= 'C100'
test['43']= "D-day" ; result_['43']= 'D000'
test['44']= "d jay" ; result_['44']= 'D200'
test['45']= "de la Rosa" ; result_['45']= 'D462'
test['46']= "Donnell" ; result_['46']= 'D540'
test['47']= "Dracula" ; result_['47']= 'D624'
test['48']= "Drakula" ; result_['48']= 'D624'
test['49']= "Du Pont" ; result_['49']= 'D153'
test['50']= "Ekzampul" ; result_['50']= 'E251'
test['51']= "example" ; result_['51']= 'E251'
test['55']= "Ellery" ; result_['55']= 'E460'
test['59']= "Euler" ; result_['59']= 'E460'
test['60']= "F.B.I." ; result_['60']= 'F000'
test['70']= "Gauss" ; result_['70']= 'G200'
test['71']= "Ghosh" ; result_['71']= 'G200'
test['72']= "Gutierrez" ; result_['72']= 'G362'
test['80']= "he" ; result_['80']= 'H000'
test['81']= "Heilbronn" ; result_['81']= 'H416'
test['84']= "Hilbert" ; result_['84']= 'H416'
test['100']= "Jackson" ; result_['100']= 'J250'
test['104']= "Johnny" ; result_['104']= 'J500'
test['105']= "Jonny" ; result_['105']= 'J500'
test['110']= "Kant" ; result_['110']= 'K530'
test['116']= "Knuth" ; result_['116']= 'K530'
test['120']= "Ladd" ; result_['120']= 'L300'
test['124']= "Llyod" ; result_['124']= 'L300'
test['125']= "Lee" ; result_['125']= 'L000'
test['126']= "Lissajous" ; result_['126']= 'L222'
test['128']= "Lukasiewicz" ; result_['128']= 'L222'
test['130']= "naïve" ; result_['130']= 'N100'
test['141']= "Miller" ; result_['141']= 'M460'
test['143']= "Moses" ; result_['143']= 'M220'
test['146']= "Moskowitz" ; result_['146']= 'M232'
test['147']= "Moskovitz" ; result_['147']= 'M213'
test['150']= "O'Conner" ; result_['150']= 'O256'
test['151']= "O'Connor" ; result_['151']= 'O256'
test['152']= "O'Hara" ; result_['152']= 'O600'
test['153']= "O'Mally" ; result_['153']= 'O540'
test['161']= "Peters" ; result_['161']= 'P362'
test['162']= "Peterson" ; result_['162']= 'P362'
test['165']= "Pfister" ; result_['165']= 'P236'
test['180']= "R2-D2" ; result_['180']= 'R300'
test['182']= "rÄ≈sumÅ∙" ; result_['182']= 'R250'
test['184']= "Robert" ; result_['184']= 'R163'
test['185']= "Rupert" ; result_['185']= 'R163'
test['187']= "Rubin" ; result_['187']= 'R150'
test['191']= "Soundex" ; result_['191']= 'S532'
test['192']= "sownteks" ; result_['192']= 'S532'
test['199']= "Swhgler" ; result_['199']= 'S460'
test['202']= "'til" ; result_['202']= 'T400'
test['208']= "Tymczak" ; result_['208']= 'T522'
test['216']= "Uhrbach" ; result_['216']= 'U612'
test['221']= "Van de Graaff" ; result_['221']= 'V532'
test['222']= "VanDeusen" ; result_['222']= 'V532'
test['230']= "Washington" ; result_['230']= 'W252'
test['233']= "Wheaton" ; result_['233']= 'W350'
test['234']= "Williams" ; result_['234']= 'W452'
test['236']= "Woolcock" ; result_['236']= 'W422'
loop i over test
say test[i].left(10) get_soundex(test[i]) '=' result_[i]
end

View file

@ -0,0 +1,18 @@
# script Soundex.ps1
Param([string]$Phrase)
Process {
$src = $Phrase.ToUpper().Trim()
$coded = $src[0..($src.Length - 1)] | %{
if('BFPV'.Contains($_)) { '1' }
elseif('CGJKQSXZ'.Contains($_)) { '2' }
elseif('DT'.Contains($_)) { '3' }
elseif('L'.Contains($_)) { '4' }
elseif('MN'.Contains($_)) { '5' }
elseif('R'.Contains($_)) { '6' }
elseif('AEIOU'.Contains($_)) { 'v' }
else { '.' }
} | Where { $_ -ne '.'}
$coded2 = 0..($coded.Length - 1) | %{ if ($_ -eq 0 -or $coded[$_] -ne $coded[$_ - 1]) { $coded[$_] } else { '' } }
$coded2 = if ($coded[0] -eq 'v' -or $coded2[0] -ne $coded[0]) { $coded2 } else { $coded2[1..($coded2.Length - 1)] }
$src[0] + ((-join $($coded2 | Where { $_ -ne 'v'})) + "000").Substring(0,3)
}

View file

@ -0,0 +1,15 @@
Function t([string]$value, [string]$expect) {
$result = .\Soundex.ps1 -Phrase $value
New-Object TypeName PSObject Prop @{ "Value"=$value; "Expect"=$expect; "Result"=$result; "Pass"=$($expect -eq $result) }
}
@(
(t "Ashcraft" "A261"); (t "Ashcroft" "A261"); (t "Burroughs" "B620"); (t "Burrows" "B620");
(t "Ekzampul" "E251"); (t "Example" "E251"); (t "Ellery" "E460"); (t "Euler" "E460");
(t "Ghosh" "G200"); (t "Gauss" "G200"); (t "Gutierrez" "G362"); (t "Heilbronn" "H416");
(t "Hilbert" "H416"); (t "Jackson" "J250"); (t "Kant" "K530"); (t "Knuth" "K530");
(t "Lee" "L000"); (t "Lukasiewicz" "L222"); (t "Lissajous" "L222"); (t "Ladd" "L300");
(t "Lloyd" "L300"); (t "Moses" "M220"); (t "O'Hara" "O600"); (t "Pfister" "P236");
(t "Rubin" "R150"); (t "Robert" "R163"); (t "Rupert" "R163"); (t "Soundex" "S532");
(t "Sownteks" "S532"); (t "Tymczak" "T522"); (t "VanDeusen" "V532"); (t "Washington" "W252");
(t "Wheaton" "W350");
) | Format-Table -Property Value,Expect,Result,Pass

View file

@ -0,0 +1,23 @@
# Project: Soundex
# Date : 2018/06/11
# Author: Gal Zsolt [~ CalmoSoft ~]
# Email : <calmosoft@gmail.com>
name = ["Ashcraf", "Ashcroft", "Gauss", "Ghosh", "Hilbert", "Heilbronn", "Lee", "Lloyd",
"Moses", "Pfister", "Robert", "Rupert", "Rubin","Tymczak", "Soundex", "Example"]
for i = 1 to 16
sp = 10 - len(name[i])
see '"' + name[i] + '"' + copy(" ", sp) + " " + soundex(name[i]) + nl
next
func soundex(name2)
name2 = upper(name2)
n = "01230129022455012623019202"
s = left(name2,1)
p = number(substr(n, ascii(s) - 64, 1))
for i = 2 to len(name2)
n2 = number(substr(n, ascii(name2[i]) - 64, 1))
if n2 > 0 and n2 != 9 and n2 != p s = s + string(n2) ok
if n2 != 9 p = n2 ok
next
return left(s + "000", 4)

View file

@ -0,0 +1,2 @@
. di soundex("Ashcraft")
A226