Re: Technique for building arguments from function name?
by Corion (Patriarch) 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. | [reply] [Watch: Dir/Any] [d/l] [select] |
|
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.
| [reply] [Watch: Dir/Any] [d/l] |
|
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'
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
> I was toying with the idea of generating HTML code ...
You might be interested to look into CGI::HTML::Functions
> What is this technique called?
Kind of Currying, but that's not necessarily implemented with AUTOLOAD.
update
Or rather Partial application
| [reply] [Watch: Dir/Any] |
|
|
Re: Technique for building arguments from function name?
by haukex (Archbishop) 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")?
| [reply] [Watch: Dir/Any] [d/l] [select] |
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.
| [reply] [Watch: Dir/Any] |
Re: Technique for building arguments from function name?
by BillKSmith (Monsignor) 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.
| [reply] [Watch: Dir/Any] [d/l] |
Re: Technique for building arguments from function name?
by AnomalousMonk (Archbishop) 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: <%-{-{-{-<
| [reply] [Watch: Dir/Any] [d/l] [select] |
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
| [reply] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
Re: Technique for building arguments from function name?
by Dallaylaen (Chaplain) 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. | [reply] [Watch: Dir/Any] [d/l] [select] |