Skip to content

Instantly share code, notes, and snippets.

@zostay
Last active September 12, 2015 03:50
Show Gist options
  • Save zostay/13a073e62520b1041ecb to your computer and use it in GitHub Desktop.
Save zostay/13a073e62520b1041ecb to your computer and use it in GitHub Desktop.
Weird Iteration/Switch Bug
Okay, so I am at a loss so far as to what is happening here, but something
broke.
In Template::Anti, the node-set.t test is failing:
t/node-set.t ...........
ok 1 - text works
ok 2 - attrib one works
ok 3 - attrib two works
ok 4 - attrib three works
not ok 5 -
# Failed test at t/node-set.t line 43
# expected: '<root><one/></root>'
# got: '<root><one/><two/><three/></root>'
not ok 6 -
# Failed test at t/node-set.t line 53
# expected: '<root><one href="http://example.com/vader">Vader</one><one href="http://example.com/sidious">Sidious</one></root>'
# got: '<root><one href="http://example.com/vader">Vader</one><two/><three/><one href="http://example.com/sidious">Sidious</one></root>'
ok 7 - modifies original
1..7
# Looks like you failed 2 tests of 7
Dubious, test returned 2 (wstat 512, 0x200)
Failed 2/7 subtests
(See https://travis-ci.org/zostay/Template-Anti#L1104 for the original.)
This is failing because the truncate() method in NodeSet.pm6 is failing to
remove <two/> and <three/> from the DOM as is expected. The code for that looks
like this:
method truncate(Int $keep = 0) {
for @!nodes -> $node {
my $kept = 0;
if $keep == 0 {
$node.nodes = ();
}
else {
for $node.nodes {
when XML::Element { .remove if $kept++ >= $keep }
when XML::Node { .remove }
default { }
}
}
}
self
}
(See
https://github.com/zostay/Template-Anti/blob/master/lib/Template/Anti/NodeSet.pm6#L177
for the original.)
The problem is that when it runs the innter loop (for $node.nodes), the block
for the `when XML::Element condition` is not being run even though there are
XML::Element nodes present. They are not falling into the XML::Node branch
either (obviously, since they would be removed if there were).
However, changing the for-loop like this instead fixes the problem:
my @nodes = $node.nodes;
for @nodes {
The question is why? Is there something wrong with using $node.nodes like this
or is something wrong with Rakudo? Or have I overlooked something else I have
done wrong here?
@zostay
Copy link
Author

zostay commented Sep 12, 2015

For some reason I was expecting (and pre-GLR, this assumption seems to have held true) that the for-loop iterated over a copy of the array. I was also under the impression that Perl 5's for-loop did the same. However, this is not true. When items in the array are deleted using .remove, some elements where being skipped during iteration.

Compare:

my @foo = 1,2,3; for @foo { .say; @foo.splice(0, 1) }

to:

my @foo = 1,2,3; for @=@foo { .say; @foo.splice(0, 1) }

The latter was basically what I was expecting to happen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment