Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid

subroutine bewilderment: how to mimic builtins

by dimar (Curate)
on Feb 07, 2006 at 22:57 UTC ( #528658=perlquestion: print w/replies, xml ) Need Help??
dimar has asked for the wisdom of the Perl Monks concerning the following question:

Consider the following code:

### sample1 $_ = 'lrep esu'; $str = reverse lcfirst uc; print $str."\n"; ### sample2 $_ = 'lrep esu'; $str = lcfirst reverse uc; print $str."\n";

Each sample produces a different result, but neither one generates an error. This is true regardless of the order in which I specify the built-in functions. Moreover, I do not have to specify parenthesis or an input argument. The functions implicitly work on $_ by default.

Question: suppose I have a custom subroutine "spaces_to_und" that converts spaces to underscores. How do I declare the subroutine so that it works *just like* the built-in lcfirst, reverse and uc functions?

The goal is to operate on $_, just like built-ins, as well as obviate the need to use parens, as well as the ability to call the functions in any relative order in the source code, without generating an error.

To pull this off, prototyping is surely required, but no proof-of-concept has yet been found. Is this even possible?


Replies are listed 'Best First'.
Re: subroutine bewilderment: how to mimic builtins
by ikegami (Pope) on Feb 07, 2006 at 23:22 UTC

    To work on $_ by default, check the size of @_. For example,
    sub func { local $_ =   @_ ? $_[0] : $_;  ... } # copy
    sub func { local *_ = \(@_ ? $_[0] : $_); ... } # alias

    To request a scalar argument, use prototypes. For example,
    sub func($)  { ... } # scalar
    sub func(;$) { ... } # optional scalar
    This is required for $str = my_uc my_reverse 'ab', 'cd'; to work.

    Finally, use wantarray to determine whether a list or a scalar (or nothing at all) should be returned.

    In Perl, your functions would be the following:

    sub pp_lcfirst(;$) { local $_ = @_ ? $_[0] : $_; s/(.)/\l$1/; return $_; } sub pp_uc(;$) { local $_ = @_ ? $_[0] : $_; s/(.*)/\U$1/; return $_; } sub pp_reverse { if (wantarray) { my @rv; push(@rv, pop(@_)) while @_; return @rv; } else { my $str = join('', @_); my $rv = ''; my $i = length($str); $rv .= substr($str, $i, 1) while $i--; return $rv; } } sub spaces_to_und(;$) { local $_ = @_ ? $_[0] : $_; s/\s+/_/g; return $_; }

    Update: Cleanup.

    Update: Fixed the typo japhy noticed in my prototypes.

      Shouldn't those $; prototypes be ;$ like spaces_to_und()'s?

      Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
      How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
      Note that ;$ doesn't parse the same as a builtin like uc:
      $ perl -we'print uc 1, 2' 12 $ perl -we'sub myuc(;$) { $_[0]||$_ } print myuc 1, 2' Too many arguments for main::myuc at -e line 1, at EOF
      Also, in the presence of my $_, myuc won't be able to see the right $_. Implementing a _ prototype is on the todo list, but not on any particular person's plate that I know of.

        I know, but it's as close as possible. Considering the case that is different is an illegal case, I didn't bother confusing the issue by mentioning it.

        my $_; doesn't compile for me with Perl v5.8.6. Did you mean tie $_? There's some kind of problem with local $_ when $_ is tied, but 1) that's not likely, and 2) it can be avoided by localizing *_ instead of $_ (as I did in one of my examples).

Re: subroutine bewilderment: how to mimic builtins
by diotalevi (Canon) on Feb 07, 2006 at 23:29 UTC

    If @_ is empty, read from $_. Check wantarray()'s value when deciding what to do or what to return.

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: subroutine bewilderment: how to mimic builtins
by revdiablo (Prior) on Feb 07, 2006 at 23:21 UTC

    Prototyping? Am I missing something here? This seems to work:

    sub mylcfirst { my $x; if (@_) { $x = join "", @_ } else { $x = $_ } $x =~ s/(.)/\l$1/; return $x; } sub myuc { my $x; if (@_) { $x = join "", @_ } else { $x = $_ } $x =~ s/(.*)/\U$1/; return $x; } sub myreverse { my $x; if (@_) { $x = join "", @_ } else { $x = $_ } $x = reverse $x; return $x; } ### sample1 $_ = 'lrep esu'; $str = myreverse mylcfirst myuc; print $str."\n"; ### sample2 $_ = 'lrep esu'; $str = mylcfirst myreverse myuc; print $str."\n";

    Sure, the code is a little clunky. And it doesn't take reverse in list context into account, but the idea is there. What part of this isn't what you are expecting?

      myuc needs a prototype to handle $str = myuc myreverse 'lrep', 'esu'; appropriately. Same for myucfirst. See my reply.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://528658]
Approved by kutsu
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (4)
As of 2017-09-21 04:02 GMT
Find Nodes?
    Voting Booth?
    During the recent solar eclipse, I:

    Results (241 votes). Check out past polls.