/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

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