Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

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

by jffry (Hermit)
on Oct 28, 2009 at 22:26 UTC ( #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 (Pope) 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
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? | Other CB clients
Other Users?
Others surveying the Monastery: (6)
As of 2021-05-15 20:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Perl 7 will be out ...





    Results (150 votes). Check out past polls.

    Notices?