Perl: 8 or 10 distinct characters
s/// solution: 10 distinct, 13 total
The (purported; see below) sed technique always works in perl, too, and yields the name number of distinct characters (10):
s/[aeiou]//gi
For example:
$ echo 'This program will remove VOWELS. So we can speak without them.' |
perl -ple 's/[aeiou]//gi'
Ths prgrm wll rmv VWLS. S w cn spk wtht thm.
That’s 10 distinct characters, as this proves:
$ echo 's/[aeiou]//gi' | perl -nle '@s{split//}=(); print scalar keys %s'
10
The problem with the sed solution is that its /i is not part of POSIX sed, and thus is not portable:
$ echo 'This program will remove VOWELS. So we can speak without them.' |
sed -e 's/[aeiou]//gi'
sed: 1: "s/[aeiou]//gi": bad flag in substitute command: 'i'
That’s running on an OpenBSD system. In contrast, because /i is indeed always part of standard perl, you can count on its always being there. Unlike sed.
If you want to include “y” in the list of vowels, it is of course one greater if you use the same technique:
$ echo 'This nifty program remove any VOWELS. So we easily can speak without them.' |
perl -ple 's/[aeiouy]//gi'
Ths nft prgrm rmv n VWLS. S w sl cn spk wtht thm.
$ echo 's/[aeiouy]//gi' | perl -nle '@s{split//}=(); print scalar keys %s'
11
And it is now 14 total characters.
tr[][] solution: 8 distinct 10 total
You could also use tr/// to remove anything it matches. Perl can even use sed’s y/// alias for tr:
y/aeiou//d
which is now 8 distinct characters, but does’t work on uppercase. You end up having to add 5 more characters to cope with the casemaps:
$ echo 'y/aeiouAEIOU//d' | perl -nle '@s{split//}=(); print scalar keys %s'
13
and of course that’s now 15 total.
However, adding “y” to the mix as a vowel doesn’t up the number of distinct characters as it did with the s/// version:
$ echo 'This nifty program remove any VOWELS. So we easily can speak without them.' |
perl -ple 'y/aeiouy//d'
Ths nft prgrm rmv n VOWELS. S w sl cn spk wtht thm.
So that’s still just the original 8 distinct out of 11 total:
$ echo 'y/aeiouy//d' | perl -nle '@s{split//}=(); print scalar keys %s'
8
EDIT: Accounting for Diacritics
And what about inputs like Renée’s naïveté? The correct output should of course be Rn’s nvt. Here’s how to do that, using v5.14’s /r flag for s///:
$ echo 'Renée’s naïveté' |
perl5.14.0 -CS -MUnicode::Normalize -nle 'print NFD($_)=~s/[aeiou]\pM*//rgi'
Rn’s nvt
That’s 27 distinct characters:
$ echo 'print NFD($_) =~ s/[aeiou]\pM*//rgi' |
perl -nle '@s{split//}=(); print scalar keys %s'
27
You can trim that to 26 if you can guarantee that you’re running at least v5.10 by swapping out the print for a say:
$ echo 'Renée’s naïveté' |
perl -Mv5.14 -CS -MUnicode::Normalize -nlE 'say NFD($_) =~ s/[aeiou]\pM*//rgi'
Rn’s nvt
$ echo 'say NFD($_) =~ s/[aeiou]\pM*//rgi' |
perl -nle '@s{split//}=(); print scalar keys %s'
26
And you can get it down to 22 if you don’t mind moving the diacritics instead of removing them:
$ echo 'Renée’s naïveté' |
perl -Mv5.14 -CS -MUnicode::Normalize -nlE 'say NFD($_) =~ s/[aeiou]//rgi'
Rń’s n̈vt́
Which is ... interesting to look at, to say the least. :) Here’s its distinct-count:
$ echo 'say NFD($_) =~ s/[aeiou]//rgi' |
perl -nle '@s{split//}=(); print scalar keys %s'
22
Good luck getting any other language to properly deal with diacritics using fewer characters than this!
Renée’s naïveté? Stripped of vowels, that should of course beRn’s nvtas output. Seem like these approaches are all pretty ahem naïve if you ask me. :) – tchrist May 28 '12 at 20:25