Element Words
April 14, 2017
My periodic table doesn’t include some of the fancy new chemicals that have been recently created:
(define elements (map string-downcase (map symbol->string '(H He Li Be B C N O F Ne Na Mg Al Si P S Cl Ar K Ca Sc Ti V Cr Mn Fe Co Ni Cu Zn Ga Ge As Se Br Kr Rb Sr Y Zr Nb Mo Tc Ru Rh Pd Ag Cd In Sn Sb Te I Xe Cs Ba La Ce Pr Nd Pm Sm Eu Gd Tb Dy Ho Er Tm Yb Lu Hf Ta W Re Os Ir Pt Au Hg Tl Pb Bi Po At Rn Fr Ra Ac Th Pa U Np Pu Am Cm Bk Cf Es Fm Md No Lr Rf Db Sg Bh Hs Mt))))
A word is an element word if its first character is in the list of elements, or its first two characters are in the list of elements, and all the remaining characters after the first or second also form an element word:
(define (eword? str) ; element-word (let loop ((cs (string->list str))) (or (null? cs) (and (member (string (car cs)) elements) (loop (cdr cs))) (and (pair? (cdr cs)) (member (list->string (take 2 cs)) elements) (loop (cddr cs))))))
For example, the word “phosphorus” can be formed as shown below, with “ho” formed either as hydrogen and oxygen or as holmium:
p phosphorous ho holmium s sulfur p phosphorous h hydrogen o oxygen ru ruthenium s sulfur
> (eword? "phosphorus") #t
We give the dictionary as a list of words; you could arrange to read the words from a file if you prefer:
(define (element-words words) (let loop ((words words) (max-len 0) (max-words (list))) (if (null? words) (reverse max-words) (let* ((word (car words)) (word-len (string-length word))) (cond ((< word-len max-len) (loop (cdr words) max-len max-words)) ((not (eword? word)) (loop (cdr words) max-len max-words)) ((< max-len word-len) (loop (cdr words) word-len (list word))) (else (loop (cdr words) max-len (cons word max-words))))))))
Here is a list of element names, which we use as a dictionary:
(define element-names (map string-downcase (map symbol->string '(Hydrogen Helium Lithium Beryllium Boron Carbon Nitrogen Oxygen Fluorine Neon Sodium Magnesium Aluminum Silicon Phosphorus Sulfur Chlorine Argon Potassium Calcium Scandium Titanium Vanadium Chromium Manganese Iron Cobalt Nickel Copper Zinc Gallium Germanium Arsenic Selenium Bromine Krypton Rubidium Strontium Yttrium Zirconium Niobium Molybdenum Technetium Ruthenium Rhodium Palladium Silver Cadmium Indium Tin Antimony Tellurium Iodine Xenon Cesium Barium Lanthanum Cerium Praseodymium Neodymium Promethium Samarium Europium Gadolinium Terbium Dysprosium Holmium Erbium Thulium Ytterbium Lutetium Hafnium Tantalum Tungsten Rhenium Osmium Iridium Platinum Gold Mercury Thallium Lead Bismuth Polonium Astatine Radon Francium Radium Actinium Thorium Protactinium Uranium Neptunium Plutonium Americium Curium Berkelium Californium Einsteinium Fermium Mendelevium Nobelium Lawrencium Rutherfordium Dubnium Seaborgium Bohrium Hassium Meitnerium))))
There are twelve element names that can be formed from the list of elements, of which the longest is phosphorus:
> (filter eword? element-names) ("carbon" "neon" "silicon" "phosphorus" "iron" "copper" "arsenic" "krypton" "tin" "xenon" "bismuth" "astatine") > (element-words element-names) ("phosphorus")
You can run the program at http://ideone.com/6woBy6.
In Python using an english dictionary of about 125000 words.
Stand back, I know Regular Expressions:
I don’t awk though and nicked that bit from ДМИТРИЙ МАЛИКОВ on Stack Exchange: https://unix.stackexchange.com/questions/24509/how-to-print-the-longest-line-in-a-file
Using Perl 5. A dictionary of words should be supplied as a filename or on STDIN.
#!/usr/bin/env perl
use strict;
use warnings;
use v5.22;
my @elements = qw(
ac al am sb ar as at
ba bk be bi bh b br
cd ca cf c ce cs cl cr co cu cm
ds db dy
es er eu
fm f fr
gd ga ge au
hf hs he ho h
in i ir fe
kr
la lr pb li lu
mg mn mt md hg mo
nd ne np ni nb n no
os o
pd p pt pu po k pr pm pa
ra rn re rh rg rb ru rf
sm sc sg se si ag na sr s
ta tc te tb tl th tm sn ti w
uub uuh uuo uup uuq uus uut uuu u
v
xe
yb y
zn zr
);
my $pattern = '^(' . join('|', @elements) . ')+$';
foreach (sort {-length($a) <=> -length($b)} <>) {
if (m/$pattern/) {
say;
last;
}
}
Builds a regular expression using element symbol information from the mendeleev library. Because re.match() returns a MatchObject (a true value) when there is a match or None (a false value), otherwise, re.match() can be used with filter() to get the words that can be built using the element symbols. max(), with key=len, returns the longest word.
For my wordlist, the longest is “otorhinolaryngologist” at 21 letters.
Here’s a Haskell version. (I’m currently too lazy to keep track of the parse.)
@Mike: Your line 6 should read:
Then the regex matches until the end of the word. The word “otorhinolaryngologist” is not a valid match (only the “o” is matched.
This reminds me of this problem: https://programmingpraxis.com/2014/07/25/number-words & here’s an adapted version of my C++ solution there – it’s essentially the recursive solution, but using dynamic programming to avoid repeated identical recursions. It also prints the number of ways found to construct each word, and piping the output through sort tells us that as before the 19-letter “nonrepresentational” is longest, but with only 2 variations, but the 17-letter “inconspicuousness” has 24 variations. Interestingly, “casinos”, with a mere 7 letters, has 13 variations:
Using an HFST (Helsinki Finite-State Tools) component to extract the matching words. Braces in the regex formalism delimit string literals, and 0 stands for the empty string which is mapped to a newline at the end of each match.
Using Bash to run the matcher and extract the longest matches.
The longest match in the document is “kirkaspiirteinen” ‘bright-featured’, at 16 characters.