Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Check exist of anonymous subroutine before calling it within regexp substitute?

by jffry (Hermit)
on Oct 28, 2009 at 22:26 UTC ( [id://803816]=perlquestion: print w/replies, xml ) Need Help??

jffry has asked for the wisdom of the Perl Monks concerning the following question:

Either I was rather clever in the past or, more likely, I had cut-n-pasted code from someone smarter than me, I recently noticed that I am handling a log formatting string in my custom logging function using a hash of subroutine references.

The relevant parts look something like this:

my %meta_char = ( '%' => sub { '%' }, c => sub { $chan }, m => sub { $in_message }, P => sub { $$ }, p => \&_get_package, s => \&_get_subroutine, t => \&_get_timedate, ); ($out_message = $log_format) =~ s/%(.)/$meta_char{$1}->()/ge;

And $out_message is the what gets output.

So if $log_format equals "%t %m", then the time is printed in front of each log message.

The problem is that if $log_format contains a format character that does not exist (for example, "%X") in the %meta_char hash, then Perl does not like that at all.

What I would like is if the code quietly ignored bad log formatting options, but how would I do that? The only thing I can think of is to add some sort of "if exists &sub" logic on the right side of that s// operation. However, Perl is not liking my attempts to do that.

Any other ideas? Thanks much.

  • Comment on Check exist of anonymous subroutine before calling it within regexp substitute?
  • Download Code

Replies are listed 'Best First'.
Re: Check exist of anonymous subroutine before calling it within regexp substitute?
by moritz (Cardinal) on Oct 28, 2009 at 22:39 UTC
    my $default = sub { '' }; ... ($out_message = $log_format) =~ s/%(.)/($meta_char{$1} || $default)->( +)/ge;

    This replaces unknown escape sequences with an empty string.

    Perl 6 - links to (nearly) everything that is Perl 6.
Re: Check exist of anonymous subroutine before calling it within regexp substitute?
by ikegami (Patriarch) on Oct 28, 2009 at 22:47 UTC
    ($out_message = $log_format) =~ s/%(.)/ if ($meta_char{$1}) { $meta_char{$1}->() } else { warn("Unrecognized format %$1\n"); "%$1" } /ge;
Re: Check exist of anonymous subroutine before calling it within regexp substitute?
by kyle (Abbot) on Oct 29, 2009 at 02:03 UTC

    If it were my code, I'd probably write it the way moritz has it, but if you're extra paranoid, you might do something like this:

    ($out_message = $log_format) =~ s{%(.)}{ my $m = $meta_char{$1}; (ref sub {} eq ref $m) ? $m->() : $1 }ge;

    That will withstand missing elements as well as elements that exist but don't have a code reference in them.

      Minor tangent...

      You swapped out the regexp delimiters from slashes to curly braces, but to me, this seems harder to read. I can only conclude that you think the curly braces improve readability. I'm guessing when you read the code, your mind's parser is thinking, "a block is a block so I expect curly braces" or something like that?

        I often write s/// replacements (and m// matches) with braces, mostly because I don't have to escape slashes, and I don't even have to escape braces as long as they're nested. It's not unusual that I want to match a string with a slash in it (usually a file path), so I find using any "not slash" as delimiters useful.

        That said, I don't make a habit of changing delimiters in existing code. I probably would have stayed with slashes in my reply here if not for the /e flag. Since the replacement is being executed as a code block, I think it's good to make it look like a code block.

Re: Check exist of anonymous subroutine before calling it within regexp substitute?
by oha (Friar) on Oct 29, 2009 at 17:55 UTC
    Just to add something more on the table, here using the experimental (??{CODE})

    %d = ( foo => sub { "[@_]"; }, bar => sub { "(@_)"; }, ); # case 1 $_ = 'foo fooz bar'; s/(\w+)/$d{$1}?$d{$1}->($1):"$1"/ge; print "$_\n"; # case 2 $_ = 'foo fooz bar'; s/(\w+)(??{!$d{$1}})/$d{$1}->($1)/ge; print "$_\n"; __OUT__ [foo] fooz (bar) [foo] [foo]z (bar)
      That should be
      s/(\w+)(?(?{ !$d{$1} })(?!))/$d{$1}->($1)/ge;

      Your solution fails if the non-existant word is followed by a one ("1"). For example, try input "baz1".

        ty, i confess that at a first look i didn't understand why, could you elaborate plz?

      Is there a plain English name for the ??{CODE} syntactic element? It is hard as heck to google, and I'm not seeing it in Perl 5.10 docs. Is it a Perl 6 or 5.11 thing?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://803816]
Approved by moritz
Front-paged by tye
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (5)
As of 2024-04-19 21:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found