Seeking advice on selecting a sub (possibly/slightly related to cmd line switches)

M

Michele Dondi

I'm not sure the subject line is appropriate. Couldn't come up with
anything better...

Well, just to be definite (but not limited to the following example),
suppose I have to accomplish different tasks according to some cmd
line switches for a script. Say all of them involve 'while (<>)
{...}', so I do *not* want to test the switch for every iteration and
I do *not* want to repeat big portions of code either.

It seems to me that the "best" solution that doesn't clobber the main
logic of the program is something along the lines of the following
example. But I'm not sure if it is the "right(TM) thing" to do: can
you comment please?

#!/usr/bin/perl
# Oversimplified example
use strict;
use warnings;
use Term::ANSIColor qw/:constants/;
use Getopt::Std;

sub doit;

my %opt;
getopts 'c', \%opt;
if ($opt{'c'}) {
*doit = sub {
local $_=shift;
print if s/\b(foo)\b/RED.$1.RESET/ge
}
}
else {
*doit = sub {
local $_=shift;
print if /\bfoo\b/;
}
}

doit $_ while <>;
__END__

Also, as an aside, and since I'm very curious, having heard that
basically in Perl6 "everything will be an object", I wonder if it will
provide hooks for the beginning and the end of a sub, or possibly for
a label inside it, as in the following pseudo-code:

sub doit {
...
}

doit.END = {
print STDERR "$.: foo" if $foo;
} if $verbose;


Michele
 
P

Perusion hostmaster

I'm not sure the subject line is appropriate. Couldn't come up with
anything better...

Well, just to be definite (but not limited to the following example),
suppose I have to accomplish different tasks according to some cmd
line switches for a script. Say all of them involve 'while (<>)
{...}', so I do *not* want to test the switch for every iteration and
I do *not* want to repeat big portions of code either.

It seems to me that the "best" solution that doesn't clobber the main
logic of the program is something along the lines of the following
example. But I'm not sure if it is the "right(TM) thing" to do: can
you comment please?

I believe it is right if it works...but you might find it more
flexible to map your subs to a hash or something. The code below
allows you to add an alternative processing routine by adding
one hash member...of course if you used Getopt::Long and some
appropriate logic you could name them as well.

#!/usr/bin/perl

# Oversimplified example
use strict;
use warnings;
use Term::ANSIColor qw/:constants/;
use Getopt::Std;

my $sub;
my %opt;

my %dispatch = (
g => sub {
my $arg = shift;
print $arg if $arg =~ s/\b(bar)\b/GREEN . $1 . RESET/ge;
},
c => sub {
my $arg = shift;
print $arg if $arg =~ s/\b(foo)\b/RED . $1 . RESET/ge;
},
default => sub {
local $_=shift;
print if /\bfoo\b/;
},
);

my $optstring = join '', grep length($_) == 1, keys %dispatch;

getopts $optstring, \%opt;

my @subs = sort keys %opt;

push @subs, 'default' unless @subs;

while(defined (my $line = <>) ) {
$dispatch{$_}->($line) for @subs;
}

__END__
 
N

nobull

Michele Dondi said:
I'm not sure the subject line is appropriate. Couldn't come up with
anything better...

It looks quite good to me.
Well, just to be definite (but not limited to the following example),
suppose I have to accomplish different tasks according to some cmd
line switches for a script. Say all of them involve 'while (<>)
{...}', so I do *not* want to test the switch for every iteration and
I do *not* want to repeat big portions of code either.

It seems to me that the "best" solution that doesn't clobber the main
logic of the program is something along the lines of the following
example. But I'm not sure if it is the "right(TM) thing" to do: can
you comment please?
if ($opt{'c'}) {
*doit = sub {
local $_=shift;
print if s/\b(foo)\b/RED.$1.RESET/ge
}
}
else {
*doit = sub {
local $_=shift;
print if /\bfoo\b/;
}
}

doit $_ while <>;

Looks OK to me, iff not being able to alter the code that calls doit()
was one of your criteria.

If not then I'd use the anon subs directly rather than munging the
sumbol table.

Also it is not necessary to use the formal paramter passing mechanism
all the time (although some people perfer to). The other way of
calling subroutines does have it's place, and this, IMHO is it.

my $doit = do {
if ($opt{c}) {
sub {
print if s/\b(foo)\b/RED.$1.RESET/ge
}
}
else {
sub {
print if /\bfoo\b/;
}
}
};

&$doit while <>;
 
E

Eric J. Roode

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I'm not sure the subject line is appropriate. Couldn't come up with
anything better...

Well, just to be definite (but not limited to the following example),
suppose I have to accomplish different tasks according to some cmd
line switches for a script. Say all of them involve 'while (<>)
{...}', so I do *not* want to test the switch for every iteration and
I do *not* want to repeat big portions of code either.

It seems to me that the "best" solution that doesn't clobber the main
logic of the program is something along the lines of the following
example. But I'm not sure if it is the "right(TM) thing" to do: can
you comment please?

#!/usr/bin/perl
# Oversimplified example
use strict;
use warnings;
use Term::ANSIColor qw/:constants/;
use Getopt::Std;

sub doit;

my %opt;
getopts 'c', \%opt;
if ($opt{'c'}) {
*doit = sub {
local $_=shift;
print if s/\b(foo)\b/RED.$1.RESET/ge
}
}
else {
*doit = sub {
local $_=shift;
print if /\bfoo\b/;
}
}

doit $_ while <>;
__END__

That looks like it'd work. I think I personally would have done it
differently; I'd put a code reference into a scalar variable, $doit, and
then run the main loop as

$doit->($_) while <>;

You could do this via dispatch table:

sub something {...la la la ...}
sub anotherthing {... la la la ...}
sub morethings { ...la la la ...}

my %dispatch = (
one => \&something,
two => \&anotherthing,
three => \&morethings,
);

my $doit = $dispatch{$option};
$doit->($_) while <>;
# or even:
Also, as an aside, and since I'm very curious, having heard that
basically in Perl6 "everything will be an object", I wonder if it will
provide hooks for the beginning and the end of a sub, or possibly for
a label inside it, as in the following pseudo-code:

sub doit {
...
}

doit.END = {
print STDERR "$.: foo" if $foo;
} if $verbose;

You can do this in Perl 5. No need to wait for hell to freeze over,
which is approximately when Perl 6 will be ready for prime time. See the
Hook::LexWrap module (although the wrappers are not "lexical" as the name
implies, but rather dynamic).

- --
Eric
$_ = reverse sort qw p ekca lre Js reh ts
p, $/.r, map $_.$", qw e p h tona e; print

-----BEGIN PGP SIGNATURE-----
Version: PGPfreeware 7.0.3 for non-commercial use <http://www.pgp.com>

iQA/AwUBPwyknmPeouIeTNHoEQIkWgCeK7k6eY9ydDezjP8Bk27xOxLa3yAAoJNx
BNf6YlcL9eGs7j8nnAOiTRF1
=+nTa
-----END PGP SIGNATURE-----
 
M

Michele Dondi

That looks like it'd work. I think I personally would have done it
differently; I'd put a code reference into a scalar variable, $doit, and [snip]
You could do this via dispatch table: [snip]
my $doit = $dispatch{$option};
$doit->($_) while <>;
# or even:
$dispatch{$option}($_) while <>;

Well, to answer to you and to the others who kindly followed-up my
post, from *my* point of view, all these alternative approaches are
substantially equivalent to my own. That is, I just wanted to know if
"assigning" a suitable coderef is a good way to implement "diversified
behaviours". Say I was quite confidedent this is in fact the case: I
just needed some confirmation...

I wrote "substantially" and not "completely" because actually I've not
played much with typeglobs and I couldn't be 100% sure there wouldn't
have been any issue with (wrt my original code)

sub doit;
...
*doit = sub { ... };

and if there are not, then I will continue using such a technique,
since I feel most comfortable with it as far as code readibility is
concerned. At this point it is largely a matter of personal taste: I
can understand that (e.g.) other people would prefer to tell
user-defined subs from builtin commands, but I prefer to always have
some "degree of freedom" with parentheses and be allowed to ignore
some. (with all my respect to LISP fans out there!)
You can do this in Perl 5. No need to wait for hell to freeze over,
which is approximately when Perl 6 will be ready for prime time. See the
Hook::LexWrap module (although the wrappers are not "lexical" as the name
implies, but rather dynamic).

Wow, I would have never thought it would be possible *now*. Even if I
have a feeling it is a "workaround", I'll try it ASAP. However, as I
said, I'm curious, and in particular I'm curious about Perl6, because
of the various "snippets" I've heard about, and OTOH, you see, this is
the kind of thing one (well, "I", for one) would like to see as a
basic language feature.


Michele
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,444
Messages
2,571,709
Members
48,796
Latest member
Greg L.
Top