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

Technique for building arguments from function name?

by nysus (Priest)
on Dec 04, 2017 at 19:29 UTC ( #1204884=perlquestion: print w/replies, xml ) Need Help??
nysus has asked for the wisdom of the Perl Monks concerning the following question:

This is probably a silly question but I'm asking it anyway. I'm pretty sure it's not possible in Perl but I'm wondering if something like this technique has a name. So let's say I have the following functions:

sub function_cat { print "cat"; } sub function_dog { print "dog"; }

Each time I want to print a new word, I write a new function.

But instead, is it possible to do something like this:

sub function_x { print $x; } function_tiger(); # yields "tiger"

Sure, I could create a generic function and do function('fish') but function_fish is so much easier. :)

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest";
$nysus = $PM . ' ' . $MCF;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re: Technique for building arguments from function name?
by Corion (Pope) on Dec 04, 2017 at 19:33 UTC

    I don't recommend this technique, but have a look at AUTOLOAD:

    our $AUTOLOAD; sub AUTOLOAD { if( $AUTOLOAD =~ /.*?::function_(\w+)$/ ) { function_x( $1 ); } else { die "Unknown function '$AUTOLOAD' called"; }; }

    Update: Perlbotics points out that my approach needs to skip main:: or other package names.

      Ah, yes, the old AUTOLOAD. You have proven, once again, that you have forgotten more Perl than I have even remembered. What are the downsides of doing this? I was toying with the idea of generating HTML code like this:

      add_h1('Title'); add_p('Here is the opening paragraph.')

      What is this technique called? There must be a name for it.

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

        What is this technique called? There must be a name for it.

        Well, this technique resembles what the ol' CGI.pm was doing in its days. I would say that it is against the DRY principle in software development - Don't Repeat Yourself - so whilst it might have a name, it surely is not a flattering one. Writing

        add_h1('Title'); add_p('Here is the opening paragraph.')

        requires two functions, while writing

        add('h1','Title'); add('p','Here is the opening paragraph.');

        requires only one, which means less code and a smaller memory footprint, at the expense of typing 2 more chars in the source for every call ('' against _). So, having a function for every tag is overkill, as haukex correctly points out.

        There are plenty of ways of generating HTML code in a more flexible way, e.g. Mason (before and after going the Moose way), Template::Toolkit and other templating solutions.

        For more overkill, but with the benefit of writing HTML in a perlish way, see Re: Perl module for RELAX NG? (shameless plug) which converts every tag of a DTD to its function, which takes a block as first argument. I use it only to write templates to be processed by a templating engine, since it is pretty slow. See also Which internal DSL are there in Perl? (Domain Specific Languages - Part 1).

        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: Technique for building arguments from function name?
by haukex (Abbot) on Dec 04, 2017 at 20:23 UTC

    Although I agree with the other posts so far - the way the AM showed is how I might have done it (documented in Function Templates) - just in the spirit of TIMTOWTDI: Currying. For example:

    sub myfunc { my $what = shift; print "<$what> (@_)\n"; } my $myfunc_dog = sub { myfunc("dog",@_) }; my $myfunc_cat = sub { myfunc("cat",@_) }; $myfunc_dog->("woof"); $myfunc_cat->("meow");

    Although I do have to say that I find pretty much all of the solutions overkill... why not just myfunc("dog","woof")?

Re: Technique for building arguments from function name?
by Laurent_R (Canon) on Dec 04, 2017 at 20:08 UTC
    This is probably not a very good idea. You should rather look at either dispatch tables or OO programming.

    I would really not recommend that (even less than AUTOLOAD), but it is certainly possible to obtain what you're looking for by tampering with the symbol table.

    Update: Tampering with the symbol tables is what Anonymous Monk suggested earlier, I had not seen this post when I wrote mine.

Re: Technique for building arguments from function name?
by BillKSmith (Vicar) on Dec 04, 2017 at 21:28 UTC
    This is not exactly what you requested, but it may capture the spirit. Create an anonymous closure for each animal. Use a meaningful names for the reference variables.
    use strict; use warnings; sub make_animal_subs { my $animal = shift; return sub{print $animal, "\n";}; } my $function_tiger = make_animal_subs('tiger'); my $function_dog = make_animal_subs('dog'); $function_tiger->(); &$function_dog(); # Or use the alternate syntax.

    UPDATE: Sorry, I had not seen haukex's similar post.

    Bill
Re: Technique for building arguments from function name?
by Anonymous Monk on Dec 04, 2017 at 19:55 UTC
    Corion's suggestion provides a function_* for every *. This one only creates the ones you list. Whatever works for you.
    for my $x (qw( cat dog fish )) { no strict 'refs'; *{"function_$x"} = sub { print $x }; } function_cat(); # print "cat" function_banana(); # error

      Ah, nifty and easy. That could be just what I need. Thanks!

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

Re: Technique for building arguments from function name?
by Dallaylaen (Hermit) on Dec 05, 2017 at 00:58 UTC

    This is likely a sign of need for designing things in a more general way (subclass or something). However, sometimes there's already an established API to follow, and generalization is only available under the hood. In such cases I do like follows:

    package Bark; use strict; use warnings; # more code here foreach (qw(cat dog)) { my $method_name = "function_$_"; # We're going to close over this variable # so it must be fresh in every loop iteration # don't use loop counter here my $impl = $_; # pre-caclucate more variables here if needed. # do whatever normal "sub $method {...}" would do # while we're still in strict mode my $code = sub { print $impl; }; # Finally, plant method - stricture is ONLY turned off # until end of scope i.e. for 1 line only no strict 'refs'; ## no critic # just in use "perlcritic" *$method_name = $code; }; 1;

    This code can be tested (and actually was) with the following line:

    perl -I. -MBark -we 'Bark->function_cat'

    Note that I use $_ as loop variable because I have to reassign it anyway or ALL generated closures refer to the same var.

    This sample is possibly in for further improvements but at least it allows to generalize code and have strictures on in the generated method/functions.

Re: Technique for building arguments from function name?
by AnomalousMonk (Chancellor) on Dec 05, 2017 at 02:00 UTC

    Just for the heck of it (and I also don't recommend it), a prototype approach (they've gotta be good for something):

    c:\@Work\Perl\monks>perl -wMstrict -le "use constant VALID_TAG => qr{ \A [[:alpha:]] [[:alpha:]\d]* \z }xms; ;; sub html (*@) { my ($tag, @strings) = @_; ;; $tag =~ s{ \A \s+ | \s+ \z}{}xmsg; die qq{invalid tag '$tag'} unless $tag =~ VALID_TAG; ;; return qq{<$tag>@strings</$tag>}; } ;; print html(p, qw(how now brown cow)); my $n = 2; print html('h' . $n, 'the rain in spain'); print html(strike, 'now', html(b, 'is the'), 'time'); ;; print html(42, 'oops...'); " <p>how now brown cow</p> <h2>the rain in spain</h2> <strike>now <b>is the</b> time</strike> invalid tag '42' at -e line 1.
    The advantage of the  * prototype is that you can use a "naked" tag string. You save an underscore at the cost of a comma and an optional space; a wash, I'd say. It even does minimal validation (but something like
        print html(qw(b now is the time));
    is accepted with an unenlightening warning).

    Update: Added  strike example to show multicharacter tag.


    Give a man a fish:  <%-{-{-{-<

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1204884]
Approved by herveus
Front-paged by Dallaylaen
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (4)
As of 2018-05-23 03:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Notices?