Mathematica, 72 65 61 bytes
Print@@@Tuples@{a=##/(b=#5#9#15#21#25#)&@@Alphabet[],b,a,b,a}
For testing, I recommend replacing Print@@@
with ""<>#&/@
. Mathematica will then display a truncated form showing the first few and last few words, instead of taking forever to print 288,000 lines.
Explanation
I finally found a use for dividing strings. :)
I've been intrigued by the possibility of adding or multiplying strings for a while, but the actual use cases are fairly limited. The main point is that something like "foo"+"bar"
or "foo"*"bar"
(and consequently, the short form "foo""bar"
) is completely valid in Mathematica. However, it doesn't really know what to do with the strings in arithmetic expressions, so these things remain unevaluated. Mathematica does apply generally applicable simplifications though. In particular, the strings will be sorted into canonical order (which is fairly messed up in Mathematica, once you start sorting strings containing letters of various cases, digits and non-letters), which is often a dealbreaker, but doesn't matter here. Furthermore, "abc""abc"
will be simplified to "abc"^2
(which is a problem when you have repeated strings, but we don't have that either), and something like "abc"/"abc"
will actually cancel (which we'll be even making use of).
So what are we trying to golf here. We need a list of vowels and a list of consonants, so we can feed them to Tuples
to generate all possible combinations. My first approach was the naive solution:
Characters@{a="bcdfghjklmnpqrstvwxz",b="aeiouy",a,b,a}
That hardcoded list of consonants does hurt a bit. Mathematica does have an Alphabet
built-in which would allow me to avoid it, if I were able to remove the vowels in a cheap way. This is where it gets tricky though. The simplest way to remove elements is Complement
, but that ends up being longer, using one of the following options:
{a=Complement[Alphabet[],b=Characters@"aeiouy"],b,a,b,a}
{a=Complement[x=Alphabet[],b=x[[{1,5,9,15,21,25}]]],b,a,b,a}
(Note that we don't need to apply Characters
to the whole thing any more, because Alphabet[]
gives a list of letters, not a string.)
So let's try that arithmetic business. If we represent the entire alphabet as a product of letters instead of a list, then we can remove letters by simple division, due to the cancelling rule. That saves a lot of bytes because we won't need Complement
. Furthermore, "a""e""i""o""u""y"
is actually a byte shorter than Characters@"aeiouy"
. So we do this with:
a=##/(b="a""e""i""o""u""y")&@@Alphabet[]
Where we're storing the consonant and vowel products in a
and b
, respectively. This works by writing a function which multiplies all its arguments with ##
and divides them by the product of vowels. This function is applied to the alphabet list, which passes each letter in as a separate argument.
So far so good, but now we have
{a=##/(b="a""e""i""o""u""y")&@@Alphabet[],b,a,b,a}
as the argument to Tuples
, and those things are still products, not lists. Normally, the shortest way to fix that is putting a List@@@
at the front, which turns the products into lists again. Unfortunately, adding those 7 bytes makes it longer than the naive approach.
However, it turns out that Tuples
doesn't care about the heads of the inner lists at all. If you do
Tuples[{f[1, 2], f[3, 4]}]
(Yes, for an undefined f
.) You'll get:
{{1, 3}, {1, 4}, {2, 3}, {2, 4}}
Just as if you had used a List
instead of f
. So we can actually pass those products straight to Tuples
and still get the right result. This saves 5 bytes over the naive approach using two hardcoded strings.
Now the "a""e""i""o""u""y"
is still fairly annoying. But wait, we can save a few bytes here as well! The arguments of our function are the individual letters. So if we just pick out the right arguments, we can reuse those instead of the string literals, which is shorter for three of them. We want arguments #
(short for #1
), #5
, #9
, #15
, #21
and #25
. If we put #
at the end, then we also don't need to add any *
to multiply them together, because (regex) #\d+
is a complete token that can't have any non-digit appended to it. Hence we end up with #5#9#15#21#25#
, saving another 4 bytes.