by Arne Sommer

# Count and Jump with Raku and Perl

[107] Published 20. December 2020.

This is my response to the Perl Weekly Challenge #091.

## Challenge #091.1: Count Number

You are given a positive number `\$N`.

Write a script to count number and display as you read it.

Example 1 ```Input: \$N = 1122234 Output: 21321314 as we read "two 1 three 2 one 3 one 4" ``` Example 2 ```Input: \$N = 2333445 Output: 12332415 as we read "one 2 three 3 two 4 one 5" ``` Example 3 ```Input: \$N = 12345 Output: 1112131415 as we read "one 1 one 2 one 3 one 4 one 5" ```

I did just this (in addition to 97 other things) in my Centenary Sequences with Raku article. See the «Look-and-Say Sequences» section at the top of Part 8: Look-and-Say and Text.

The «look-and-say» procedure, as used below, is copied verbatim from the program with the same name.

File: count-number ```#! /usr/bin/env raku subset PositiveInt of Int where * > 0; # [1] unit sub MAIN (PositiveInt \$N); say look-and-say(\$N); sub look-and-say (\$input) { my \$return = ""; for \$input.comb: / (.) \$0* / -> \$batch # [2] { \$return ~= \$batch.chars ~ \$batch.substr(0,1); # [3] } return \$return; } ```

[1] It does not make sense to allow positive numbers (as stated in the challenge), so I restrict the input to positive integers only.

[2] As long as we can get blocks of identical letters (1 or more),

[3] • Add the count and the character.

#### The magic Comb

The `comb` method will, when used without arguments, return a Sequence containing all the letters in the string separately:

```> "122333444".comb.raku ("1", "2", "2", "3", "3", "3", "4", "4", "4").Seq ```

When given a number, the returned strings will have that length (or less, as in the final one):

```> "122333444".comb(2) ("12", "23", "33", "44", "4").Seq ```

We can also use a Regex. The regex is applied to the original string, and whatever matches is the first value in the returned Sequence. Then this goes on, until there are no more characters in the string.

This one matches a single digit at a time, giving the same Sequence as initially. As long as there are only digits in the string:

```> "122333444".comb(/\d/).raku ("1", "2", "2", "3", "3", "3", "4", "4", "4").Seq > "122HELP333444ME!!!".comb(/\d/).raku ("1", "2", "2", "3", "3", "3", "4", "4", "4").Seq ```

The second one shows that it silently ignores characters that does nop match.

We want to extract groups of the same digit, and can do that with a backwards reference (`\$0`) to the first match (`(.)`) like this:

```> "122333444".comb(/(.)\$0*/).raku ("1", "22", "333", "444").Seq ```

We could have used `\d` here, but `.` is shorter. We have already ensured that the input is a positive integer, so it does not really matter which one we use here.

See docs.raku.org/routine/comb for more information about `comb`.

Running it:

```\$ ./count-number 1122234 21321314 \$ ./count-number 2333445 12332415 \$ ./count-number 12345 1112131415 ```

This does not work, as the input is not a positive number:

```\$ ./count-number 00 Usage: ./count-number <N> ```

### A Perl Version

Perl does not have `comb`, and `split` is no good as it removes the delimiter character(s). It is possible to do this with a Regex (as shown in this Rosetta Code article - and included as «count-number-perl-regex» in the zip file), but I am unable to come up with this kind of cleverness on my own.

So the hard way it is:

File: count-number-perl ```#! /usr/bin/env perl use strict; use warnings; my \$N = shift(@ARGV) || ""; die "Please specify a positive integer" unless \$N =~ /^[1-9]\d*\$/; my @input = split("", \$N); my \$current = shift(@input); my \$count = 1; while (@input) { if (\$input[0] eq \$current) { shift(@input); \$count++; } else { print \$count . \$current; \$current = shift(@input); \$count = 1; } } print \$count . \$current . "\n"; ```

Running it gives the same result as the Raku version:

```\$ ./count-number-perl 1 2 1 2 11 \$ ./count-number-perl 1122234 21321314 \$ ./count-number-perl 2333445 12332415 \$ ./count-number-perl 12345 1112131415 ```

## Challenge #091.2: Jump Game

You are given an array of positive numbers `@N`, where value at each index determines how far you are allowed to jump further.

Write a script to decide if you can jump to the last index. Print 1 if you are able to reach the last index otherwise 0.

Example 1 ```Input: @N = (1, 2, 1, 2) Output: 1 as we jump one place from index 0 and then twoe places from index 1 to reach the last index. ``` Example 2 ```Input: @N = (2,1,1,0,2) Output: 0 it is impossible to reach the last index. as we jump two places from index 0 to reach index 2, followed by one place jump from index 2 to reach the index 3. once you reached the index 3, you can't go any further because you can only jump 0 position further. ```
File: jump-game-sans-zero ```#! /usr/bin/env raku subset PositiveInt of Int where * > 0; # [1] unit sub MAIN (*@N where @N.elems > 0 && all(@N) ~~ PositiveInt; # [2] my \$index = 0; loop # [3] { ( say 1; last ) if \$index == @N.end; # [4] @N[\$index].defined # [5] ?? ( \$index += @N[\$index] ) # [5a] !! ( say 0; last); # [5b] } ```

[1] Positive numbers does not make much sense, so I have restricted the input to positive integers.

[2] Ensure that we have at least one element, and that they all satisfy the restriction set up in [1].

[3] An eternal loop. We have two exit stretegies, in [4] and [5].

[4] Have we reached the end of the array? If so print "1" (as in «hurrah») and exit.

[5] Have we jumped past the array end? If so print "0" (as in «bummer») and exit [5b]. If not, add the number at that position to the index [5a], ready for the next iteration (and check in [4]).

Note the postfix «if» in [4] and the ternary «if» (`??` and `!!`) in [5]. You have probably read somewhere that you can only have one expression before a postfix «if», and that is almost true. You can only have one block, and using the grouping operator `(` and `)` allows us to add as much code as we want. And yes, you can use `{` and `}` instead, if that makes you feel better.

``` { say 1; last } if \$index == @N.end; # [4x] ```

Running it:

```\$ ./jump-game 1 2 1 2 1 \$ ./jump-game 2 1 1 0 2 Usage: ./jump-game [<N> ...] ```

Oops.

The problem is the «0», which is illegal as per the challenge. So the challenge should have said «non-negative integers `@N`».

Let us so just that:

File: jump-game ```#! /usr/bin/env raku subset PositiveInt0 of Int where * >= 0; unit sub MAIN (*@N where @N.elems > 0 && all(@N) ~~ PositiveInt0); my \$index = 0; loop { (say 1; last ) if \$index == @N.end; @N[\$index].defined && @N[\$index] # [1] ?? ( \$index += @N[\$index] ) !! ( say 0; last); } ```

[1] The last part is there to ensure program termination. We would have an eternal loop without it.

Running it:

```\$ ./jump-game 1 2 1 2 1 \$ ./jump-game 2 1 1 0 2 0 ```

### Perl

This is a straight forward translation of the Raku version.

File: jump-game-perl ```#! /usr/bin/env perl use strict; use feature 'say'; use List::Util qw(all); die "Non-negative integers only" unless all { \$ ~= /^\d+\$/ } @ARGV; my \$index = 0; while (1) { if (\$index == @ARGV -1) { say 1; last; } if (defined \$ARGV[\$index] && \$ARGV[\$index]) { \$index += @ARGV[\$index]; } else { say 0; last; } } ```

Running it gives the same result as the Raku version:

```\$ ./jump-game-perl 1 2 1 2 1 \$ ./jump-game-perl 2 1 1 0 2 0 ```

And that's it.