I’ve been doing the Perl Weekly
Challenges. The
latest
involved breaking down numbers and following sequences. (Note that
this is open until 20 December 2020.)
TASK #1 › Count Number
You are given a positive number $N
.
Write a script to count number and display as you read it.
The examples make this clearer. For each subsequence of identical digits,
emit (the length of that subsequence) followed by (the digit), the
whole lot to be concatenated into a single string. (As usual, the
question of why one might want to do this is elided.)
This is the Perl weekly challenge so I do it with regular
expressions.
sub cn {
my $n=shift;
my @j=$n =~ /((.)\2*)/g;
This is a little bit awkward, because I end up capturing both the full
subsequence ("333") and the individual character ("3").
my @out;
while (@j) {
push @out,length(shift @j);
push @out,shift @j;
}
return join('',@out);
}
Raku returns a bunch of match objects, so I can stringify them and
just pull out the full subsequence.
my @j=$n ~~ m:g/((.)$0*)/;
my @out;
for @j -> $match {
my $q=$match.Str;
@out.push(chars($q));
@out.push(substr($q,0,1));
}
(Why $0
rather than $1
? I have no idea.)
Python is more like Perl, though it gives me a list of lists rather
than a flat list.
m=re.findall(r'((.)\2*)',n)
out=list()
for mch in m:
out.append(str(len(mch[0])))
out.append(mch[1])
and Ruby looks remarkably like Python. (Obviously the use of the
intermediate variable j
is a holdover from the Perl code; for all
the other languages I could use the regexp evaluation directly as a
loop iterator.)
def cn(n)
j=n.scan(/((.)\2*)/)
out=Array.new
j.each do |x|
out.push(x[0].length)
out.push(x[1])
end
return out.join
end
Rust is a bit different, because its built-in regexp library doesn't
do backreferences (which makes it faster, and they aren't often
needed). There's an external library which can do them, but I wanted
to keep things simple. So instead I just iterate through the string,
noting when the character changes. Yeah, probably the if count>0
clause should have gone in a subroutine rather than being duplicated.
fn cn(n: String) -> String {
let mut out="".to_string();
let mut ch='x';
let mut count=0;
for c in n.chars() {
if c == ch {
count += 1;
} else {
if count>0 {
out.push_str(&(count.to_string()));
out.push_str(&(ch.to_string()));
}
ch=c;
count=1;
}
}
if count>0 {
out.push_str(&(count.to_string()));
out.push_str(&(ch.to_string()));
}
return out;
}
TASK #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.
Again this is made clearer by the examples. Start at index 0. Add to
the current index the value at that index. Repeat. If you will
eventually reach the final index, return 1, otherwise return 0.
The examples give two outcomes: a successful completion (1,2,1,2)
,
or a system which will hit a value of 0 (not, I note, a positive
number!), producing an infinite loop (2,1,1,0,2)
. Another possible
outcome is a jump beyond the end of the array, which I assume must
also return 0: (2,1)
.
Well, that's pretty straightforward:
sub jg {
my $n=shift;
my $mx=$#{$n};
my $p=0;
while (1) {
$p+=$n->[$p];
The success case: landed on the final entry.
if ($p == $mx) {
return 1;
The failure cases: run off the end or landed on a zero. If we were
allowed negative numbers I'd also check for the case $p < 0
. If the
setter intended that running off the end counted as a success, the
changes would be obvious.
} elsif ($p > $mx || $n->[$p] == 0) {
return 0;
}
}
}
The other languages are basically the same except for minor
differences of syntax.
Full code on
github.
Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.