aboutsummaryrefslogtreecommitdiff
path: root/challenge-163/james-smith/README.md
blob: 8922aac73018059fa5eac0caa4216d53504b3874 (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
[< Previous 162](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-162/james-smith) |
[Next 164 >](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-164/james-smith)
# The Weekly Challenge 163

You can find more information about this weeks, and previous weeks challenges at:

  https://theweeklychallenge.org/

If you are not already doing the challenge - it is a good place to practise your
**perl** or **raku**. If it is not **perl** or **raku** you develop in - you can
submit solutions in whichever language you feel comfortable with.

You can find the solutions here on github at:

https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-163/james-smith

# Challenge 1 - Sum bitwise operator

***You are given list positive numbers, `@n`. Write script to calculate the sum of bitwise `&` operator for all unique pairs.***

## The solution

As with all code there is:

 * an initial phase where we set the sum to 0.
 * a processing phase where we compute the sum:
   * In this we repeatedly shift the first element of the list and:
   * and it with all remaining elements keeping the sum.
 * a final phase where we just return this sum.

If we can't guarantee there are no duplicates in the list we add an 
extra part to the initial phase to remove these duplicates. Note we include the `map { $_ + 0 }` to guarentee that the numbers are all numbers rather than strings.
If they get treated as strings then `15 & 16` becomes `('1'&'1').('5'&'6') = '14'` rather than `0`.

This gives us:

```perl
sub bit_sum {
  my $t = 0;

  my %hash = map { $_ => 1 } @_;
  @_ = map { $_ + 0 } keys %hash;

  while(@_>1) {
    my $a = shift;
    $t+= $a&$_ for @_;
  }

  $t;
}

```

We can write a shorter version by converting the outer loop and the initial phase into a C-style for loop.

```perl
sub bit_sum_splat {
  for($a=0,(%^H=map{$_=>1}@_),(@_=map{$_+0}keys%^H),$b=shift;@_;$b=shift){
    $a+=$b&$_ for@_;
  }
  $a;
}
```

***Notes:*** to remove the need for `my` and to resolve a related issue (`my` in comma-separated statements) we use
some special variables which we don't use here (in the special way) `$a`, `$b` the sort variables and `%^H` the only
special hash that you can safely use in most cases. Note we have to do the `$b=shift` twice - once before the first
loop and once after each loop, it's just the way that the C-loop works...

### "Unreadable" versions of the code.

Below are code equivalent to the above - but written in as compact as possible. To avoid `my` we use the special
variables `%^H`, `$a` and `$b`. All of which are safe to be written to (usually!). Note although `shift` feels
the most natural way of executing the code - in fact it doesn't matter which way we reduce the array so we can
also use `pop` which is two bytes shorter..

```perl
#--------1---------2---------3---------4---------5---------6---------7---------8
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
sub bsm{$a=0;%^H=map{$_,1}@_;@_=keys%^H;$b=0+pop,map{$a+=$b&$_}@_ while@_;$a}         ## 77 chars
sub bsu{$a=0;$b=pop,map{$a+=$b&$_}@_ while@_;$a}                                      ## 48 chars
```

# Challenge 2 - Summations

***You are given a list of positive numbers, `@n`. Write a script to find out the summations as described below.***

## Solution.

For a given row we drop the first element, then the remaining cells are the cumulative sum of the previous row. *e.g.*

```
  a   b   c   d
      b   b+c b+c+d
          b+c b+c+b+c+d
              b+c+b+c+d = 2b+2c+d
```

Similarly to the first element we at each interation shift of the first element.

 * This time our initial phase is empty (except for showing the input when dumping the table)
 * Our loop phase:
   * throws away the first entry (`shift`)
   * initializes the cumulative sum (`t=0`)
   * computes the next row `@_ = map { $t+$_ } @_`
   * {we just `say @_` at each loop if we want to see the table}
 * The final stage again just returns the last element of the array - the total we want.

```perl
sub summation_with_table {
  my $t;
  say "@_";
  shift, ($t=0), say join ' ', @_ = map { $t+=$_ } @_ while @_>1;
  shift;
}
```

If we don't need the table but just the final value at the end, we can simplify this to:

```perl
sub summation {
  my $t;
  shift, ($t=0), @_ = map { $t+=$_ } @_ while @_>1;
  shift;
}
```

In both these challenges we use the *perl*ism that within a postfix loop we can stitch together multiple statements into a single statement by the use of `,` (or any operator usually `||`, `&&` or `?:`). This leaves the beauty of the postfix loop while allowing multiple statements.

### Pretty print.

The print above is rudimentary to say the least, this does a pretty print version with columns lining up.

```perl
sub summation_with_pretty_table {
  my ($in,$t)='';
  say map { sprintf ' %11d', $_ } @_;
  ($in.='            '),shift, ($t=0), say $in, map { sprintf ' %11d', $_ } @_ = map { $t+=$_ } @_ while @_>1;
  shift;
}
```

### No so "pretty print"... 

Below are code equivalent to the above - with all spaces removed - we use the special variable `$a` to store the total to avoid `my` again.
```perl
#--------1---------2---------3---------4---------5
#2345678901234567890123456789012345678901234567890
sub sum{shift,$a=0,@_=map{$a+=$_}@_ while@_>1;$a}      ## 49 chars