It'd be interesting to see how true this is in reality. I'm not at all convinced that most scripting languages can do this quite as concisely as bash and the Unix userland. (But, being honest, this problem seems very well aligned to that tooling's strenghts.)
Here is a Perl solution for comparison.
use strict;
use warnings;
my $limit = shift @ARGV;
my %count;
for my $line (<>) {
while ($line =~ /(\w+)/g) {
$count{lc $1}++;
}
}
@words = sort {$count{$b} <=> $count{$a} or $a cmp $b} keys %count;
$limit = @words if @words < $limit;
print "$_\t$count{$_}\n" for @words[0..($limit-1)]; sub MAIN($limit = Inf) {
my %bag is Bag = words.map(&lc);
say "{.key}\t{.value}" for %bag.sort( {
$^b.value cmp $^a.value || $^a.key cmp $^b.key
} )[^$limit]
}
And for what it's worth, the `words` function is lazy, so it won't read all of the words into memory first. %bag.sort({ -.value, .key }) import re
import sys
import collections
words = re.findall('[a-z]+', sys.stdin.read().lower())
c = collections.Counter(words)
for word, count in c.most_common(int(sys.argv[1])):
print(f'{count:>7} {word}')
Three times the character count of the Unix pipe version, but IMHO a lot more readable (and generalisable). import re
import sys
import collections
c = collections.Counter()
for line in sys.stdin:
words = re.findall('[a-z]+', line.lower())
c.update(words)
for word, count in c.most_common(int(sys.argv[1])):
print(f'{count:>7} {word}') puts STDIN.read
.split(/[^A-Za-z]+/)
.group_by(&:downcase).transform_values(&:size)
.sort_by(&:last).reverse
.take(ARGV[0].to_i)
.map{ |l| l.join("\t") }.join("\n")
Wordier than the bash script, but still pretty short and fairly trivial. Anyone could make this in five minutes. tr -cs A-Za-z '\n' |
tr A-Z a-z |
sort |
uniq -c |
sort -rn |
sed ${1}q
The real benefit is that each is a single command that is easy to test in isolation and it's multi-process. That's not possible in most scripting languages.What provides the pipe functionality?
Nothing prevents you from unit testing in scripting languages. Multi-process, sure, but most people aren't looking for that.
Ok, there's a single shell substitution, if it was fixed would you still call it a script? Technically the result of that is itself a sed script "3q", but if you count either of those then there isn't a lot of wiggle room between script and command, the arguments to tr are by far the most complex "script" involved.
> Nothing prevents you from unit testing in scripting languages.
That is a world away from what I'm talking about. Each line of that command can be executed on the CLI in isolation, you'd be replicating a lot more in nearly any scripting language, except maybe perl and awk.
> Multi-process, sure, but most people aren't looking for that.
Neither am I generally, still quite nice when you get it for free though.
The real lesson is to program using the most powerful tools at your disposal.