http://www.perlmonks.org?node_id=814291

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

Esteemed monks,

In my laziness, impatience, and hubris I have reached the conclusion that this question is worthy of your attention. If I am mistaken, please put the blame on one of the aforementioned personality traits.

I'd like to access the result of a pattern match as an array without assigning it to a variable first. Doing this the non-lazy way goes like this:

@matches = "hello awesome" =~ /(el).*(om)/; print pop @awesome;

but this is clearly unacceptable.

I've tried a couple of different approaches, which give different errors:

The Book $(man perldata) mentions that the Great Creator of perl has given it 'three built-in data types: scalars, arrays of scalars, and associative arrays of scalars, known as "hashes"'. However I see now that there are some quantum particles whose existence proves such dogma to be somewhat oversimplified. The psalm $(perldoc -f scalar) does provide a solution to my practical concerns:

$ perl -we 'print ${["hello awesome" =~ /(el).*(om)/]}[1];' om
but also mentions that "There is no equivalent operator to force an expression to be interpolated in list context because in practice, this is never needed."

This revelation suggests that there may be a better way to do this, and also increases my hunger for understanding of the Great Mysteries of perl.

And so it is with humilityhubris that I submit my request for knowledgeassume that my lack of understanding of this aspect of perl is due to a flaw in the documentation, not in my (lack of)? reading of it. Or rather ask for clarification of this aspect of the voluminous and esoteric Literature from those appointed as its interpreters.

Thank you, o great esteemed Monks.

Replies are listed 'Best First'.
Re: accessing the result of a match as an array
by JadeNB (Chaplain) on Dec 24, 2009 at 22:09 UTC
    @matches = "hello awesome" =~ /(el).*(om)/; print pop @awesome;
    but this is clearly unacceptable.

    Particularly since you match into @matches, but pop @awesome. :-) As

    prototype "CORE::pop"; # => ;\@
    indicates, if pop takes any argument at all, then it must be an array (not a list)—in common parlance, it must start with a @ sigil. (In fact, its suitability for feeding to pop is one of the ways that people usually advise to distinguish an array from a list.) The reason for this, in turn, is (I think—but this is a difficult point, so I make it small so as to avoid giving offence if I am mistaken) exactly the one you have hit on—a list is not a data structure, in the sense that it is essentially internal to the interpreter and not meant for the user to be slinging around via such tactics as naming it, or, especially, mutating it (which is what pop does).

    Since you clearly have no need for the array/list (because you don't want to name it), it's not important that you actually mutate it; so why not just index into it?

    print ( ( "hello awesome" =~ /(el).*(om)/ )[-1] );
    (Since I always forget them, I'll point out that the parentheses are necessary to avoid parsing as ( print ("hello awesome" =~ /(el).*(om)/) )[-1], which is, as perl will gently remind you, a syntax error.)

    UPDATE: By the way, [doc://perldata] and [doc://scalar] become, respectively, perldata and scalar.
    UPDATE 2: On further thought, I realise that I've never understood this error:

    Type of arg 1 to pop must be array (not list assignment) at -e line 1, + near "/(el).*(om)/)"
    That is, I understand that there is a difference between an array and a list assignment; but, since a list assignment returns the array being assigned to:
    $ perl -e '( my @a = (1) ) = (2); print "@a\n";' 2
    I don't understand why pop complains. I suspect it's a parsing problem; maybe one of the internals experts could be enticed to say for sure?

      And just for the record:

      >perl -wMstrict -le "print pop @{[ 'hello awesome' =~ /(el).*(om)/ ]}; " om

      Update: And with 5.10 regex one can also do:

      >perl -wMstrict -le "'hello awesome' =~ /(?<m>el).*(?<m>om)/; print $-{m}[-1]; " om

      (See %- in perlvar.)

        What's the advantage of your latter snippet over

        perl -e '"hello awesome" =~ /(el).*(om)/ and print $2;'
        or, if you don't like to count,
        perl -e '"hello awesome" =~ /(el).*(om)/ and print $^N;'

        By the way, while I'm nattering about linking, [doc://perlvar#%-] (but not [doc://perlvar#%25-], which over-escapes) facilitates the OP's laziness: % .

        UPDATE: It seems to me that the link text for [doc://perlvar#%-] should be '%-', but it's actually '% '. Is this a bug? (Oops, that's right: - is parsed as a word separator, as in [doc://perlrun#Location-of-Perl].)

      Hey, thanks. That helped to clarify it a bit. I actually was not aware of the ( match ) [ index ] syntax. I tried some similar stuff but never got it quite right, and wondered if I was still in the right ballpark at all.

      That's pretty much what I was looking for in the clean practical solution that I suspected the existence of. Also yeah thanks for pointing out my typing gaffe with the @awesome. And the doc:// shortcuts are great to know about, actually I see that there are a lot of interesting things happening in that section.

      I am still curious about the guts of it, ie what you mentioned in your second update, but will for now simply accept that perl works in mysterious ways. Thanks again!

Re: accessing the result of a match as an array
by ikegami (Patriarch) on Dec 24, 2009 at 23:44 UTC
    The question is wrong. Don't use two captures if you're only need one.
    print "hello awesome" =~ /el.*(om)/;
Re: accessing the result of a match as an array
by Marshall (Canon) on Dec 24, 2009 at 23:43 UTC
    The case #1 says start with "el", match that, then allow any number of random characters while still allowing the "om" to match. It did that.

    Case #2 is similar, except that the letters in-between "el" and last "om" are captured and printed.

    Case #3: Your "pop" statement is nonsensical. I have no idea what you mean by this. Perhaps you meant to do that with "@matches"? I will demo that below.

    Case #4 splits as "sentence" into 2 words.

    #!/usr/bin/perl -w use strict; #case 1 my @matches = "hello awesome" =~ /(el).*(om)/; print "@matches\n"; #prints el om #case 2 @matches = "hello awesome" =~ /(el)(.*)(om)/; print "@matches\n"; #prints el lo awes om # (el)(lo awes)(om) #case 3 nonense!!! #print pop @awesome; #nonsensical ! #there is no array "awesome" and #therefore there is nothing to "pop" from. #case 4 @matches = split (/\s/,"hello awesome"); print "@matches\n"; #prints" hello awesome"
    Update:I printed this in perhaps a more understandable way:
    #!/usr/bin/perl -w use strict; #case A my @matches = "hello awesome home" =~ /(el).*(om)/; foreach (@matches) { print $_,"\n"; } ## el ## om #case B @matches = "hello awesome home" =~ /(el)(.*)(om)/; foreach (@matches) { print $_,"\n"; } ## el this is the (el) ## lo awesome h now there is another "om" before last om ## om this is last (om) #Case C #lets say that we only wanted this stuff between the first (el) #and the last (om)? And we just want a scalar. #this is done with an array slice. my $stuff = ("hello awesome home" =~ /(el)(.*)(om)/)[1]; print "$stuff\n"; #prints "lo awesome h" #Case D my @stuff = "hello awesome home" =~ /(el)(.*)(om)/; print pop (@stuff)."\n"; #prints: om print pop (@stuff)."\n"; #prints: lo awesome h print pop (@stuff)."\n"; #prints: el #Case E @stuff = "hello awesome home" =~ /(el)(.*)(om)/; print shift (@stuff)."\n"; #prints: el print shift (@stuff)."\n"; #prints: lo awesome h print shift( @stuff)."\n"; #prints: om

      I guess the question was kind of vague.. Really I was just looking for a way to avoid a variable assignment and improve my golf scores, and maybe understand why perl didn't like what I was doing.

      print ( ( "hello awesome" =~ /(el).*(om)/ ) [1] ); is pretty much it. And yeah it was a trumped-up example that I didn't even bother to make realistic, to the point of not even looking it enough to notice that I totally spaced on the array name in the second statement.

Re: accessing the result of a match as an array
by zwon (Abbot) on Dec 24, 2009 at 22:17 UTC

    pop requires array as first argument, it won't work with lists or anything else. As a solution I can suggest the following:

    perl -we 'print "hello awesome" =~ /(?:el).*(om)/;'