Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

perl6 phasers and a 1 liner

by RichardJActon (Novice)
on Feb 13, 2018 at 17:32 UTC ( #1209076=perlquestion: print w/replies, xml ) Need Help??
RichardJActon has asked for the wisdom of the Perl Monks concerning the following question:

Greetings oh wise ones,

I was trying to write a one liner in perl6 to do something very like grep -f patternFile.txt searchFile.txt but applied to a single column within the search file.

perl6 -ne 'BEGIN {my @ids="patternFile.txt".IO.lines}{my @F=lines.split("\t"); if @F[0] (elem) @ids.Set {@F.join("\t").say}}' searchFile.txt

I noticed that the automatic splitting flag -a is not longer available so generated @F the longhand way.

but it seems i can't get to @ids from within second set of curly braces - i've not quite wrapped my head around some of the new scope rules in perl6 so tried fiddling with a few things in that area but didn't make any progress.

I've also not seen any example perl6 1 liners which use the BEGIN{}{}END{} structures so though that might have someting to do with it.

Any suggestions?

What is true is already so. Owning up to it doesn't make it worse. - Eugene Gendlin

Replies are listed 'Best First'.
Re: perl6 phasers and a 1 liner
by Laurent_R (Canon) on Feb 13, 2018 at 21:49 UTC
    You might consider these examples for some clues:
    perl6 -e 'my $c; BEGIN { $c = 1 }; say $c;' 1
    As you can see, the declaration of the $c variable can be before the BEGIN block.

    Or you can have:

    perl6 -e 'my $c; BEGIN $c = 1; say $c;' 1
    Here we see that the BEGIN phaser (or other phasers) does not need to be a block, it can be a simple statement.

    In some cases at least, the BEGIN phaser can be even a simple expression:

    perl6 -e 'my $c = BEGIN 3 - 2; say $c;' 1
    Or you can do even this:
    perl6 -e 'my $c; say $c; BEGIN $c = 1' 1
    Now a more complete example somewhat looking like what you're trying to do. I have a short Perl 6 script (file while_done.pl6) in my current directory and want to print the lines where either of the words say and while is present.
    perl6 -ne 'my $c; BEGIN {$c = qw<while say>.Set}; my $line = $_; $line +.say if $_ (elem) $c for $line.words' while_done.pl6 while True { say $line; say 'Done!';
    And this also works the same way:
    perl6 -ne 'my $c = BEGIN qw<while say>.Set; my $line = $_; $line.say i +f $_ (elem) $c for $line.words' while_done.pl6
    I guess the line would be printed twice if either of the searched words appears twice, but that was just a quick example.

    BTW, as already mentioned by Anonymous Monk, note that it is probably better to populate a set within the BEGIN block, rather than an array, because, otherwise, Perl will have to coerce the array into a set for each call to the (elem) operator and this might be inefficient if your input file is large.

Re: perl6 phasers and a 1 liner
by Anonymous Monk on Feb 13, 2018 at 19:57 UTC

    the thing you're missing here is that my variables are limited in scope to the curlies that contain them. You can, however, use phasers without curlies, too. that would solve your problem nicely, i expect. other than that, you can have @ids be a state instead of my variable, which lets you get "initialize only once" semantics without using a phaser.

    Another problem is that you have a -ne (i.e. run once for every line) but you use the lines sub in your code, which will immediately give you all lines as one list. Then the split method on that will turn it into a string for you (by concatenating all the lines) and then you split by "\t". That means that @F[0] will only contain the very first field of the very first line, and @F.join("\t").say will output the whole file (if the first field of the first line is contained in the patternFile.txt). What you probably wanted was to have my @F = $_.split("\t").

    Another thing is that you really want to compute @ids.Set once rather than for every single line. For a Set, however, you have to use either a %-sigiled variable and bind (:=) the Set (because assignment will turn the right-hand side into a Hash for you) or a $-sigiled variable and assign the Set. Binding doesn't work with state variables, though, so you're left with this:

    perl6 -ne 'state $ids = "patternFile.txt".IO.lines.Set; my @F = $_.split("\t"); if @F[0] (elem) $ids { @F.join("\t").say }'

    Hope that helps!

      Another problem is that you have a -ne (i.e. run once for every line) but you use the lines sub in your code, which will immediately give you all lines as one list.
      You seem to have missed that the code is using two different files. The OP uses the .lines IO method to read patternFile.txt in the BEGIN block, and the -ne command line option to read searchFile.txt.
        i was actually referring to the lines sub, not the lines method. both occur in the code, and the part that uses the sub is the one that is the problem.
Re: perl6 phasers and a 1 liner
by RichardJActon (Novice) on Feb 14, 2018 at 14:36 UTC

    Thank You Monks,

    I setteled on:

     perl6 -ne 'my $ids; BEGIN {$ids="patternFile.txt".IO.lines.Set}; my @F=$_.split("\t"); if @F[0] (elem) $ids {@F.join("\t").say}' searchFile.txt

    Much as Anonymous Monk suggested but with Laurent_R's use of declaring $ids outside of the BEGIN block / before the BEGIN phaser. I think that Anonymous Monk's original suggestion would have re-read the patternFile on every line which would be inefficient.

    In retrospect I clearly had an equivocation in my mental model of what lines does a I used it to pull in all the lines for the first file and then tried to used it a bit like <> from perl5 to read in 1 line at a time from STDIN.

    Thanks again!

    What is true is already so. Owning up to it doesn't make it worse. - Eugene Gendlin
      Since this is a one-liner, you might want to shorten it a bit. Please note that a method call without invocant will be invoked with $_.

      So you could change:

      my @F=$_.split("\t");
      to
      my @F= .split("\t");
      Similarly you appear to be printing the whole line. So, rather than:
      {@F.join("\t").say}
      you could have:
      {.say}
      I think that Anonymous Monk's original suggestion would have re-read the patternFile on every line which would be inefficient.
      Probably not. A state variable is initialized only once. From the documentation (https://docs.perl6.org/syntax/state):
      However, initialization happens exactly once the first time the initialization is encountered in the normal flow of execution. Thus, state variables will retain their value across multiple executions of the enclosing block or routine.
      But this should probably be confirmed with a test.

      Update: added the word "variable" which was missing in one sentence ("A state variable ...")

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1209076]
Approved by johngg
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (3)
As of 2018-08-17 21:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Asked to put a square peg in a round hole, I would:









    Results (184 votes). Check out past polls.

    Notices?