/challenge-033/james-smith/README.md
https://github.com/manwar/perlweeklychallenge-club · Markdown · 129 lines · 99 code · 30 blank · 0 comment · 0 complexity · e5cf82b0151e80b7e2249c3b5c31a691 MD5 · raw file
- 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;
- ```