Executing the one liner to process CSV a line at a time from stdin:
perl6 -ne 'my @a; $_.split(",").kv.map: {@a[$^k]+=$^v}; say @a; ENTER {say "ENTER"}; BEGIN {say "BEGIN"}; LEAVE {say "LEAVE"}; END {say "END"}';
Typing in:
1,1
1,1
^D
Gives the following output:
BEGIN
ENTER
1,1
[1 1]
1,1
[2 2]
LEAVE
END
Here we can see that the one liner is not a block executed multiple times as the ENTER and LEAVE phaser are only executed once.
This makes sense as the variable @a is accumulating. If the one liner was a block the value of @a would be reset each time.
My question is how does the topic variable $_ get updated? The topic variable is a Str (at least that's what $_.^name says). How does its value update without re-entering the block?
What am I missing?
When you add -n
it adds a for
loop around your code.
You think it adds one like this:
for lines() {
# Your code here
}
The compiler just adds the abstract syntax tree nodes for looping without actually adding a block.
(
# Your code here
) for lines()
(It could potentially be construed as a bug.)
To get it to work like the first one:
( # -n adds this
-> $_ { # <-- add this
# Your code here
}( $_ ) # <-- add this
) for lines() # -n adds this
I tried just adding a bare block, but the way the compiler adds the loop causes that to not work.
In general ENTER
and LEAVE
are scoped to a block {}
, but they are also scoped to the “file” if there isn't a block.
ENTER say 'ENTER file';
LEAVE say 'LEAVE file';
{
ENTER say ' ENTER block';
LEAVE say ' LEAVE block';
}
ENTER file
ENTER block
LEAVE block
LEAVE file
Since there is no block in your code, everything is scoped to the “file”.
The -n
command line argument puts a loop around your program,
for $*ARGFILES.lines {
# Program block given on command line
}
whereas the program execution phasers you used (BEGIN
and END
), are run once either at compile time or after the program block has finished, so they will not be part of the loop at run time.
The ENTER block phaser will run at every block entry time, whereas the
the LEAVE block phaser will run at every block exit time. So these phasers will be run for each line read in the for
loop.
Update -- Rakudo 2020.10
Running your original accumulator code (using the -ne
linewise flag) gives the following result. Note how the word "final" appears in every line:
~$ perl6 -ne 'my @a; $_.split(",").kv.map: {@a[$^k]+=$^v}; say @a, " final"; ENTER {say "ENTER"}; BEGIN {say "BEGIN"}; LEAVE {say "LEAVE"}; END {say "END"};' drclaw.txt
BEGIN
ENTER
[1 1] final
[2 3] final
[3 6] final
LEAVE
END
Below, running essentially duplicate scripts back-to-back with the -ne
flag gives an interesting result. BEGIN
, ENTER
,LEAVE
, and END
show up in the exact same location, duplicated on the order of once-per-call:
~$ perl6 -ne 'my @a; .split(",").kv.map: {@a[$^k]+=$^v}; say @a, " final_a"; ENTER {say "ENTER"}; BEGIN {say "BEGIN"}; LEAVE {say "LEAVE"}; END {say "END"}; my @b; .split(",").kv.map: {@b[$^k]+=$^v}; say @b, " final_b"; ENTER {say "ENTER"}; BEGIN {say "BEGIN"}; LEAVE {say "LEAVE"}; END {say "END"};' drclaw.txt
BEGIN
BEGIN
ENTER
ENTER
[1 1] final_a
[1 1] final_b
[2 3] final_a
[2 3] final_b
[3 6] final_a
[3 6] final_b
LEAVE
LEAVE
END
END
However, removing the -ne
flag below lets you run a for lines() {...}
loop within the Raku code itself (single script, not duplicated back-to-back). This result seems more in line with what you were expecting:
~$ perl6 -e 'my @a; for lines() {.split(",").kv.map: {@a[$^k]+=$^v};}; say @a, " final"; ENTER {say "ENTER"}; BEGIN {say "BEGIN"}; LEAVE {say "LEAVE"}; END {say "END"};' drclaw.txt
BEGIN
ENTER
[3 6] final
LEAVE
END
I think the short answer to your questions is that Phasers respect Block/Loop semantics, but are limited script-wise as to how many times they will report back to the implementer (apparently only once per call). But the ultimate difference is that the return to the user is linewise for the -ne
command line flag, as compared to an internal for lines() {...}
loop sans the -ne
command line flag.
Finally, you can always force the reloading of the $_
topic variable with the andthen
infix operator. Maybe this is what you were looking for all along:
~$ perl6 -e 'my @a; for lines() {.split(",").kv.map: {@a[$^k]+=$^v} andthen $_.say }; say @a, " final"; ENTER {say "ENTER"}; BEGIN {say "BEGIN"}; LEAVE {say "LEAVE"}; END {say "END"};' drclaw.txt
BEGIN
ENTER
(1 1)
(2 3)
(3 6)
[3 6] final
LEAVE
END
[Test file under analysis, below].
~$ cat drclaw.txt
1,1
1,2
1,3
https://docs.raku.org/language/operators#index-entry-andthen
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With