1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
Solutions by James Smith.
# Challenge 1 - Counting letters
There are two ways to solve this -> split and count or using tr... one is short code - one is longer - but much faster.
## Version 1 - short code
Uses lc, split and grep to count the elements and put them in a hash... looping through each file a line at a time with <>
```perl
use feature 'say';
use strict;
my %T = map { $_=>0 } foreach 'a'..'z';
while(<>) {
$T{$_}++ foreach grep { /[a-z]/ } split m{}, lc $_;
}
say "$_: $T{$_}" foreach 'a'..'z';
```
Running this over 13Mbytes of PHP takes approximately 6.5 seconds...
## Version 2 - faster code
Now counting letters in a string is quickest using the tr or y operator - as this requires the number of characters
changed. Without using eval you can't unfortunately sub in a variable into the pattern unlike with m/s... So we
either need to use string eval (which is evIl) or manually replicate the loop - $T{'a'} =~ y/aA/aA/ etc - in this
code... Note we set $/ to undef so that we slurp the whole file in in one go (to improve performance of using tr)
and less modifications to the %T hash...
```perl
use feature 'say';
$/=undef;
while(<>) {
$T{'a'} += y/aA/aA/;
$T{'b'} += y/bB/bB/;
..
..
$T{'y'} += y/yY/yY/;
$T{'z'} += y/zZ/zZ/;
}
say $_,': ',$T{$_}||0 foreach 'a'..'z';
```
OK - so didn't want to type 26 lines so used this one liner to do it for me!
```bash
perl -E 'say " \$t{'"'"'$_'"'"'} += y/$_".uc($_)."/$_".uc($_)."/;" foreach "a".."z";'
```
This now runs in approxy 0.25 seconds a big improvement...
## Version 3 - nicer output...
The version 3 code just expands the version 2 code - but creates a "histogram" to show the distribution (and at the same time formats the totals better)
```
a : 584193 : ##########################
b : 108267 : ####
c : 287124 : #############
d : 272798 : ############
e : 877936 : ########################################
f : 209371 : #########
g : 152944 : ######
h : 200641 : #########
i : 546465 : ########################
j : 15133 :
k : 50049 : ##
l : 326976 : ##############
m : 214631 : #########
n : 438874 : ###################
o : 436059 : ###################
p : 282120 : ############
q : 19825 :
r : 551144 : #########################
s : 552344 : #########################
t : 724711 : #################################
u : 260233 : ###########
v : 68882 : ###
w : 80759 : ###
x : 57019 : ##
y : 115201 : #####
z : 11021 :
```
# Challenge 2 - multiplication square...
Again going to extend the challenge to make this generic (in case someone wants a different version)
Hidden in the solution above was getting the number of digits for a number (so we can format the totals) - we do this again to get the size of the left hand column and the main table columns.
```perl
my $sl = int(log($N)/log(10)+1); ## Get size of integer $N - defines the width of the LH column
my $sr = int(2*log($N)/log(10)+1); ## Get size of $N squared - defines the width of other columns
```
and we use this to tweak the formats and the padding/line drawing elements!
```perl
#!/usr/bin/perl
use strict;
use feature 'say';
## This solves more than the puzzle - but thought I would make it more generic!
## This gets the size of the square that we want to display...
my $N = shift =~ s{\D}{}gr || 11; ## Default to 11 - but use first parameter as size of square!
my @R = 1..$N; ## Create a "range array" - we use this 4 times!!!
## Get width of columns for use in the renderer..
my $sl = int( log($N) / log(10) + 1); ## Get size of integer $N - defines the width of the LH column
my $sr = int( 2 * log($N) / log(10) + 1); ## Get size of $N squared - defines the width of other columns
my $fl = sprintf ' %%%dd |', $sl; ## Create a template for the first column..
my $fr = sprintf ' %%%dd', $sr; ## .... and for the other columns!
## Finally we render - make a use of sprintf with the templates and '$' x $ to generate padding
say ' ' x $sl, 'x |', ## Header (LH side)
map { sprintf $fr, $_ } @R; ## (column headers)
say join '-', '-' x $sl, '-+', ## Separator (LH side)
map { '-' x $sr } @R; ## (RH side)
say sprintf( $fl, $a=$_ ), ## Body of table (LH headers)
map { $a>$_ ? ' ' x ($sr+1) : sprintf $fr, $a*$_ } @R ## (content of row)
foreach @R;
```
|