aboutsummaryrefslogtreecommitdiff
path: root/challenge-059/james-smith/README.md
blob: 6d816375c54374b0f9b48287bfc7b07470990471 (plain)
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;
```