**Advent Calendar 2021**

### | **Day 6** | **Day 7** | **Day 8** |

The gift is presented by **James Smith**. Today he is talking about his solution to **“The Weekly Challenge - 107”**. This is re-produced for **Advent Calendar 2021** from the original **post** by `James Smith`

.

## Task #1: Self-descriptive Numbers

Write a script to display the first three self-descriptive numbers. As per **wikipedia**, the definition of `Self-descriptive Number`

is

In mathematics, a self-descriptive number is an integer m that in a given base b is b digits long in which each digit d at position n (the most significant digit being at position 0 and the least significant at position b−1) counts how many instances of digit n are in m.

To describe a number we write down the number of `0s`

, `1s`

, `2s`

, `3s`

, etc

A self-descriptive number is one of length n such that in `base n`

the description above is the number itself.

Generating self descriptive numbers can be split between the case where `n >= 7`

and `n < 7`

… As we are asked for the first three these all exist in the case where `n < 7`

….

For `n >= 7`

the self-descriptive numbers are of the form:

`n-4`

, `21`

, `0 {n-7 times}`

, `1000`

;

In our solution we loop through numbers starting `1`

to see if they are self descriptive.

To get the desciption of a number of `length k`

, we loop from `0..(k-1)`

counting the number of each digit in the string… We can do this with a harcoded series of `tr///s`

but if we want something dynamic we can write this as

```
@Q = m{($_)}g; $count = scalar @Q;
```

we can do this in one line by using `@{[ ]}`

to convert to the list of matches to an arrayand counting it by getting the `scalar`

of the `array`

… Often in **Perl** `lists`

and `arrays`

are interchangeable - but here is one of those subtle distinctions you have to be aware of.

The description is obtained by stitching those counts together… We can do this in the one-line `join q()`

, `map`

below.. We just store it the list if the description and the number are the same….

```
use strict;
use warnings;
use feature qw(say);
use Test::More;
my ($c,@res) = 0;
while( ++$c && @res<3 ) {
push @res, $c if $c == join q(),
map { scalar @{[ $c=~m{($_)}g ]} } 0 .. -1 + length $c;
}
say "@res";
```

We can reduce this further - by rewriting the inner if with using the `&&`

trick. `&&`

is evaluated lazily - so that if the left hand side is false then the right hand side is not evaluated.

So `if($x) { y() }`

can be written as `$x && y()`

;

Similarly - `unless($x) { y() }`

can be written as `$x || y()`

and `if($x) { y() } else { z() }`

can be written `$x ? y() : z()`

This means we can make the statement inside the loop a single statement and postfix the while…

```
($c,@res) = 0;
( $c == join q(),
map { scalar @{[ $c=~m{($_)}g ]} }
0 .. -1 + length $c
) && push @res, $c while ++$c && @res<3;
say "@res";
```

Note we have to wrap the `"condition"`

in brackets to force it to be evaluated before the `&&`

as otherwise the line ends in `0 .. -1 + length( $x && push @res, $c);`

This is why we write the `"yoda"`

looking `-1 + length $c`

as if you write `length $c - 1`

this evaluates to `length($c-1);`

I wouldn’t do this in `"normal"`

code as I think it can get confusing `$x && f()`

is not obviously a piece of logic, especially if `f()`

has implicit side effects as here.

If we don’t want to capture the values - but just display the results - we can drop this into a perl 1-liner on the command line.

```
perl -E '($c-join"",map{0+@{[$c=~/($_)/g]}}0..-1+length$c)||++$n&&say$c while++$c&&$n<3'
```

or

```
perl -E '($c-join"",map{0+(@Q=$c=~/($_)/g)}0..-1+length$c)||++$n&&say$c while++$c&&$n<3'
```

You will notice we are using slightly different tricks here… (Mainly we can do these because we haven’t enabled strict!! something you rarely do in Perl 1-liners…)

#### * We use -E (rather than -e) this enables more modern perl features - including usefully say!

#### * We don’t collect results - and we just keep a counter - this time we use || and && in the “logic”…

#### * We know ++$n is always going to be true (it starts off explicitly as 0 so in the condition it is going to be 1, 2, 3, …) and so we always run say$c if we get to the ++$n…

#### * Note here - this is a place where it is important to choose ++$n rather than the more common $n++, as the latter evaluates to 0 the first time it is invoked - meaning we would skip the first answer…

#### * We show a different trick to count the elements of the list.

```
- We can use another trick other than the scalar @{[ ]} trick to convert the list into an array. We store it in an array variable which forces to an array rather than a last - we can then get the length of the array (we just throw the array away!)
- As we are keeping the code short - we can replace the keyword scalar with a simple 0+ which forces the array to be converted into a scalar (and hence returns its length)
- To gain another character as the equality is numeric we can rewrite if($a==$b) { f() } as ($a-$b)||f().
$a-$b is non-zero (true) if $a!=$b
$a-$b is zero (false) if $a==$b
So we rewrite
if( $a == $b ) { f() } as unless( $a - $b ) { f() }
which we know we mentioned we could rewrite as:
($a-$b) || f()
The brackets are important o/w this evaluates to:
$a-($b||f())
which isn't what we want...
```

If you have any suggestion then please do share with us perlweeklychallenge@yahoo.com.