Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Is there another way to get named captures

by AlexP (Monk)
on Jan 08, 2022 at 19:14 UTC ( #11140275=perlquestion: print w/replies, xml ) Need Help??

AlexP has asked for the wisdom of the Perl Monks concerning the following question:

Hello monks!
I wonder if there is another way to get named captured groups than %+ or %-.

Something like:

%nc = $str =~ /?<status>[\w\040]*)/; say %nc{status};

I'm trying to rewrite my code without globals like %+, %-, because I need to pass this hash to a sub and now it looks:

my %matches = %+; sub(\%matches);
Maybe you know related modules or other solutions.

Replies are listed 'Best First'.
Re: Is there another way to get named captures
by LanX (Sage) on Jan 08, 2022 at 20:40 UTC
    > I wonder if there is another way to get named captured groups than %+ or %-.

    not without reimplementing it

    > and now it looks:

    my %matches = %+; sub(\%matches);

    why not just

    func( { %+ } );

    this will pass the hashref of a flat copy, without messing with the globally tied %+

    update

    demo in debugger

    DB<18> use Data::Dumper DB<19> sub func { say Dumper \@_} DB<20> 'ABC' =~ /(?<a>\w)(?<b>\w)(?<c>\w)/; func({%+}) $VAR1 = [ { 'a' => 'A', 'b' => 'B', 'c' => 'C' } ];

    NB: if you are only passing the named captures, why not just flattening %+ to a list?

    DB<22> sub func { my %matches = @_; say Dumper \%matches} DB<23> 'ABC' =~ /(?<a>\w)(?<b>\w)(?<c>\w)/; func(%+) $VAR1 = { 'a' => 'A', 'b' => 'B', 'c' => 'C' };

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

    edit

    s/sub/func/ , since sub is a keyword

      Thank you! I've forgotten this:

      func( { %+ } );

      It's great solution.

Re: Is there another way to get named captures
by Fletch (Bishop) on Jan 08, 2022 at 21:06 UTC

    As it shows in perlvar if you use English there's also %{^CAPTURE} and %{^CAPTURE_ALL} respectively:

    ## Being lazy and using the Mojo "ojo" oneliner for it's dumper r() $ perl -Mojo -MEnglish -E '$_ = q{foo = bar};m{(?<name>\w+) \s* = \s* +(?<value>.*) $}x; say r(\%{^CAPTURE})' { "name" => "foo", "value" => "bar" }

    That being said (IMHO) eliminating every single magic variable isn't really getting you that much of a gain for the extra typing you're setting yourself up to do (${^CAPTURE}{name} vs $+{name}). Some things are just idiomatic perl and going out of your way to make things more cumbersome to type for (again, IMHO) minor "readability" improvements isn't really helpful.

    Edit: Or if you read perlvar more carefully it mentions Tie::Hash::NamedCapture which lets you create a named alias which was exactly what you'd asked for. Derp. Since this lets you give the tied hash a semantically meaningful name yourself I'd say that's marginally more valid, but still a bit of . . . not overkill, but maybe more effort than it's worth for a (small) benefit over $+{foo}.

    $ perl -Mojo -MTie::Hash::NamedCapture -E '$_ = q{foo = bar};m{(?<name +>\w+) \s* = \s* (?<value>.*) $}x; tie my %h, q{Tie::Hash::NamedCaptur +e}; say r(\%h)' { "name" => "foo", "value" => "bar" }

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      > %{^CAPTURE}

      is just %+ by another name° and wouldn't solve the OP's issue with it having global sideeffects, which could lead to unexpected results.

      DB<34> 'ABCD' =~ /(?<a>\w)(?<b>\w)(?<c>\w)(?<d>\w)/; $h_matches = \% +{^CAPTURE}; say Dumper $h_matches $VAR1 = { 'b' => 'B', 'c' => 'C', 'a' => 'A', 'd' => 'D' }; DB<35> x $h_matches 0 HASH(0x2fa07f0) empty hash # oh oh ...

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

      °) not an alias tho, they have different refs

        Aaah I've apparently missed (as in misread) what they're looking for and why (thinking that they're objecting to the punctuation-ness). Once you mentioned that I was thinking maybe the tied version captured %+ at the point of tying but experimenting shows it's affected by the global state so no dice there.

        I could maybe imagine roughing out some sort of "matching state" context object that captures (ha) all of this stuff and returns an independent instance but that's getting back along the lines of your sample in that you'd need to use something else rather than native m{} or what not.

        use Hypothetical::MatchState qw( matchit ); my $match1 = matchit( $_ => $regex ); my $match2 = matchit( $other => $regex ); say $_->named( q{foo} ) for ( $match1, $match2 );

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

Re: Is there another way to get named captures
by AlexP (Monk) on Jan 10, 2022 at 13:22 UTC

      Thanks for writing this!

      Personally, I think there could be a small enhancement, to be able to also create match objects without needing the regular expression, like:

      my $string = 'foo foo fooo'; while( $string =~ /(fo+)/g) { my $match = Regex::Object->collect(); push @matches, $match; }

      That way, the collecting of the match data is available even when I'm not running a plain match.

        Great idea! I'll think about implementation!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11140275]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others perusing the Monastery: (4)
As of 2022-06-29 06:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My most frequent journeys are powered by:









    Results (96 votes). Check out past polls.

    Notices?