John M. Dlugosz has asked for the wisdom of the Perl Monks concerning the following question:

$name =~ s/^([$@%&<*])//; my $sigil= $1 || '&';
Given a full name, remove the prefix and store it elsewhere, taking a default if necessary.

This is fairly clear as two lines, but I wonder if there is a more elegant way to say it as one statement.

Replies are listed 'Best First'.
(tye)Re: Elegant way to parse an optional prefix with default?
by tye (Sage) on Nov 08, 2002 at 16:35 UTC

    You've hit a non-DWIMism. $@ is getting expanded into the value of that scalar. If you escape the $ then you can do:    my( $sigil )= ( $name =~ /^([\$@%&<*])/, '&' );

            - tye (just ignore this answer to the wrong question)
      Yow! I didn't know it did that in character classes.

      Interesting... this relies on undef being dropped in list catenation, so '&' becomes the first element?

        Whether to treat $ and @ as literals or not inside a regular expression is the single most complicated case of DWIM in Perl. It calculates weights to make this decision. I hate that it results in /[$]/ being a syntax error.

        And it is good that character classes are not exempted as $anagram !~ /[^\Q$word\E]/ is actually useful code.

        Yes, if you turn on warnings you'll get "Use of uninitialized value in pattern match (m//)".

                - tye
•Re: Elegant way to parse an optional prefix with default?
by merlyn (Sage) on Nov 08, 2002 at 05:18 UTC
    $name =~ s/^[$@%&<*]//; my $sigil= $1 || '&';
    Actually, that doesn't work. You don't have a $1 there. If you added the parens to make it work, I still don't quite see how to get it into one line. Nor would I worry about that.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      Hmm, it looks like (with the parens, sorry about the typo) $1 is not undef but left to an old value if there is no match?!

      So I do need to put something on the first line to test for success failure, at least. (or update the regex to make $1 success but blank)

      My ugly one line is:

      my $sigil= ($name =~ s/^([$@%&<*])//) ? $1 : '&';
      as I couldn't get it to work with my ($sigil)= ... in list context to return matches. I guess that doesn't work with replacements as opposed to matches.

      —John

Re: Elegant way to parse an optional prefix with default?
by shotgunefx (Parson) on Nov 08, 2002 at 08:35 UTC
    Never assume $1 and friends have a valid value unless the assignment is conditional on the match. I learned the hard way.

    -Lee

    "To be civilized is to deny one's nature."
Re: Elegant way to parse an optional prefix with default?
by BrowserUk (Patriarch) on Nov 08, 2002 at 06:05 UTC

    I'm not sure it qualifies as elegant, but it does do it in one line for all the cases I thought of.

    Update: I just realised that the regex below is more complex than it need be; this works too.

    Update2: Removed redundant |& (from the top version)

        my($sigil, $name) = ('&'.$_) =~ m/^&?([\$\@%&<*])(.*?)>?$/;

    #! perl -sw use strict; for (qw/$foo @foo %foo &foo <foo> *foo foo/) { my($sigil, $name) = ('&'.$_) =~ m/^(?:&?([\$\@%&<*]|&))(.*?)>?$/; print "$sigil:$name\n"; } __END__ c:\test>temp $:foo @:foo %:foo &:foo <:foo *:foo &:foo c:\test>

    Nah! You're thinking of Simon Templar, originally played (on UKTV) by Roger Moore and later by Ian Ogilvy
      I don't get it... isn't  ... ( [\$\@%&<*] | & ) ... redundant? Anything in the class or a & (which is already in the class).

        Spot on John. (wishing I could claim it was my deliberate mistake:)

        A left over from an earlier attempt that didn't work, but like the a doctor, it did no harm, so it got left behind.

        Without that and the previous unnecessary complications, it begins to approach a (small) degree of elegance.


        Nah! You're thinking of Simon Templar, originally played (on UKTV) by Roger Moore and later by Ian Ogilvy
Re: Elegant way to parse an optional prefix with default?
by fglock (Vicar) on Nov 08, 2002 at 12:05 UTC

    This one seems to work. It doesn't modify $name:

    for $name (qw/$foo @foo %foo &foo <foo> *foo foo/ ) { my $sigil= ($name =~ /^([\$\@%&<*])/) ? $1 : "&"; print "$name = $sigil\n"; } $foo = $ @foo = @ %foo = % &foo = & <foo> = < *foo = * foo = &