One Step Beyond: Perl Weekly Challenge #112
I’m diving headlong into the new Perl Weekly Challenge.
TASK #1 › Canonical Path
Submitted by: Mohammad S Anwar
You are given a string path, starting with a slash‘/'
.Write a script to convert the given absolute path to the simplified canonical path.
In a Unix-style file system:
- A period
'.'
refers to the current directory- A double period
'..'
refers to the directory up a level- Multiple consecutive slashes (‘//’) are treated as a single slash
'/'
The canonical path format:
- The path starts with a single slash
'/'
.- Any two directories are separated by a single slash
'/'
.- The path does not end with a trailing
'/'
.- The path only contains the directories on the path from the root directory to the target file or directory
I solved it with regular expressions. I could imagine an iterative solution, where we split on /
and go through, dropping the entry if it is .
, that and decrementing the index if it’s ..
and so on, but really, the regular expression route seems the easiest to read and implement.
I used a while loop instead of a global regex, because the result of one change could be hidden. Take the third example, /a/b/c/../..
. s{/\w+/\.\.}{}gmix
would only match /c/..
, not the one that would become /b/..
.
Show Me The Code!
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw{ say state postderef signatures };
no warnings qw{ experimental };
my @paths;
push @paths, "/a/";
push @paths, "/a/b/./c/";
push @paths, "/a/b//c/";
push @paths, "/a/b/c/../d/..";
push @paths, "/a/b/c/../..";
push @paths, "/a/b/c/";
for my $path (@paths) {
my $cpath = canonical_path($path);
say <<"END";
path: $path
canonical: $cpath
END
}
sub canonical_path ($path) {
while ($path =~ m{/\w+/\.\.}mix
|| $path =~ m{//}mix
|| $path =~ m{/\./}mix )
{
$path =~ s{/\w+/\.\.}{/}mix;
$path =~ s{//}{/}mix;
$path =~ s{/\./}{/}mix;
}
$path =~ s{/$}{}mix;
$path = qq{/$path} unless $path =~ m{^/}mix;
return $path;
}
path: /a/
canonical: /a
path: /a/b/./c/
canonical: /a/b/c
path: /a/b//c/
canonical: /a/b/c
path: /a/b/c/../d/..
canonical: /a/b
path: /a/b/c/../..
canonical: /a
path: /a/b/c/
canonical: /a/b/c
TASK #2 › Climb Stairs
Submitted by: Mohammad S Anwar
You are given$n
steps to climbWrite a script to find out the distinct ways to climb to the top. You are allowed to climb either 1 or 2 steps at a time.
What do I always say?
This Looks Like A Job For Recursion!
This looked very much like a job for recursion to me. You either take two steps or one. For each, either:
- you there’s still steps, so you take another one
- you’re a the top of the stairs, and so we count that one
- you’ve taken more steps than there are, so we don’t count that
(I really gotta make merch.)
Unlike some previous tasks, the steps are not interchangable. 1 step + 2 steps
is distinct from 2 steps + 1 step
.
Show Me The Code!
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw{ say state postderef signatures };
no warnings qw{ experimental };
my @values = ( 3, 4, 5 );
@values = @ARGV if @ARGV;
for my $v (@values) {
my $c = 1;
say '-' x 20;
my @steps = climb_stairs($v);
say qq{INPUT: $v};
say qq{OUTPUT: } . scalar @steps;
for my $opt (@steps) {
say qq{\tOption $c: $opt};
$c++;
}
}
sub climb_stairs ( $v, $max_steps = 2 ) {
my @output;
for my $n ( 1 .. $max_steps ) {
my $step = $n < 2 ? '1 step' : "$n steps";
my $w = $v - $n;
if ( $w > 0 ) {
push @output,
map { $step . ' + ' . $_ }
climb_stairs( $w, $max_steps );
}
elsif ( $w == 0 ) { push @output, $step; }
}
return @output;
}
--------------------
INPUT: 3
OUTPUT: 3
Option 1: 1 step + 1 step + 1 step
Option 2: 1 step + 2 steps
Option 3: 2 steps + 1 step
--------------------
INPUT: 4
OUTPUT: 5
Option 1: 1 step + 1 step + 1 step + 1 step
Option 2: 1 step + 1 step + 2 steps
Option 3: 1 step + 2 steps + 1 step
Option 4: 2 steps + 1 step + 1 step
Option 5: 2 steps + 2 steps
--------------------
INPUT: 5
OUTPUT: 8
Option 1: 1 step + 1 step + 1 step + 1 step + 1 step
Option 2: 1 step + 1 step + 1 step + 2 steps
Option 3: 1 step + 1 step + 2 steps + 1 step
Option 4: 1 step + 2 steps + 1 step + 1 step
Option 5: 2 steps + 1 step + 1 step + 1 step
Option 6: 1 step + 2 steps + 2 steps
Option 7: 2 steps + 1 step + 2 steps
Option 8: 2 steps + 2 steps + 1 step