Rabbitholeing
With PWC 182-2 specifically asking for Linux paths to be handled, we need to resolve issues like /../
and symbolic links. Since I didn’t feel like putting a directory called a
into my root folder, I wrote a little helper that deals with some of the tripwires that modern filesystems provide.
my @input = qw <
/a/b/c/1/x.pl
/a/b/c/d/e/2/x.pl
/a/b//c/d/3/x.pl
/a/b/./c/4/x.pl
/a/../a/b/c/d/5/x.pl
>;
sub resolve(Str:D() $s){
my @parent-refs = (my @parts = $s.split(rx{ ‘/./’ | ‘/’+ })).pairs.grep(*.value eq '..')».key;
@parent-refs = flat @parent-refs, @parent-refs »-» 1;
@parts[@parent-refs]:delete;
@parts.join(‘/’)
}
# OUTPUT: /a/b/c/1/x.pl /a/b/c/d/e/2/x.pl /a/b/c/d/3/x.pl /a/b/c/4/x.pl ///a/b/c/d/5/x.pl
The last path starts with a triple root, because join
assumes holes are actually there. It won’t skip fields that are Empty
either, so is default(Empty)
doesn’t help. To actually remove elements from an Array
we are better of with splice
. Since this method doesn’t return self
, we can’t just @parts.splice(@parent-refs.any,1).join(‘/’)
. Both ways to remove elements are mutators and eager. That doesn’t fit well into the rest of the language and spells doom for concurrency. To find a solution I had to go down the rabbit hole that is iteration in Rakudo. The bottom happens to be located in Rakudo/Iterator.pm6.
method pull-one() is raw {
nqp::ifnull(
nqp::atpos($!reified,++$!i),
nqp::if(
nqp::islt_i($!i,nqp::elems($!reified)), # found a hole
self!hole($!i),
IterationEnd
)
)
}
So a hole in an Array
is just a null-pointer in C-land — given that we didn’t overshoot the end of the Array
. With that knowledge, building an Iterator that skips elements becomes rather simple.
multi sub prune(@a, Int:D $i --> Seq:D) {
prune @a, $i .. $i
}
multi sub prune(@a, +@l is copy --> Seq:D) {
@l = @l.sort; # this makes checking the index against the needles simpler
Seq.new: class :: does Iterator {
has int $!i;
has $!reified;
submethod !SET-SELF(\arr) {
$!reified := nqp::getattr(@a,List,'$!reified');
$!i = -1;
self
}
method new(\arr) { nqp::create(self)!SET-SELF(arr) }
method pull-one is raw {
loop {
++$!i;
if @l {
@l.shift while +@l && $!i > @l[0].max;
next if +@l && @l[0].min ≤ $!i ≤ @l[0].max;
}
return nqp::ifnull(
nqp::atpos($reified, $!i),
nqp::if(
nqp::isge_i($!i, nqp::elems($reified)),
IterationEnd,
next # we actually got a hole
)
);
}
}
}.new(@a)
}
@a = ('a' .. 'z').List;
dd @a.&prune( 25 );
dd @a.&prune( 10..15 );
dd @a.&prune( (2,3,10..15, 21..22, 25).pick(5) ); # randomising for testing
# OUTPUT: ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y").Seq
# ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z").Seq
# ("a", "b", "e", "f", "g", "h", "i", "j", "q", "r", "s", "t", "u", "x", "y").Seq
The idea is to loop by default and bail out of that loop if the current index held in $!i
is not found in any needle. Since we don’t got real goto
in Raku, loop/next
becomes a decent substitute. As I don’t really understand what a unreified Array
is, I’m right now not qualified to provide a PR.
Dealing with lazy and infinite lists makes Array
a beast. I shall not falter until I have tamed it!
-
October 3, 2022 at 15:162022.40 Learning to Core – Rakudo Weekly News