5

The following Perl code fails on the second attempt of dereferencing the capturing group "ext"

use strict;
use warnings;

foreach(qw/2.1 2/){
#foreach(@ARGV){
    my $out= sprintf("%.2f", $_);
    $out =~ s/(?:(?<ext>\.[1-9]?)|\.0)0([^\w]|$)/$+{'ext'}/g;
    print ":".$out.":\n";
}

How can I set a default for the capturing group within the replacement string in case it doesn't get defined?

I'm sure there are several other ways to solve this problem, but not being able to set a default value for a capture group will surely come up another time again I believe - so please help me figure this out.

FOLLOWUP:

..I got it to work with the help of ikegami's advice, so that it reads

$out =~ s{(?:(?<ext>\.[1-9]?)|\.0)0([^\w]|$)}{ $+{'ext'} // "" }eg;

...it's just isn't there another way? Especially since this only works with Perl's "e"valuate regex functionality. This must come up in standard regex way as well, to at least ignore the capture group dereferencing if it wasn't captured in the first place, no?

pilcrow
  • 56,591
  • 13
  • 94
  • 135
mcaustria
  • 123
  • 6
  • Re "*isnt there another way?*", There are no other way to "use a default for the capturing group". You necessarily need to run code. You can't make a computer program do something without code. You want to run this code for reach replacement, and `e` is how you run code to generate the replacement. – ikegami Jul 03 '23 at 22:27
  • What are you trying to do ?? Why are you using the //e anyway ? What exactly are you trying to replace ? Whats the purpose of capture group 2 there ? – sln Jul 03 '23 at 23:11

2 Answers2

6

Make the replacement part an expression using the e modifier.

s{...}{ $+{ext} // "default" }eg

However, what you are trying to achieve does not require the use of captures, much less conditional captures and default values for them.

It appears that you are trying to remove all trailing zeroes, and the trailing . that might remains afterwards. For that, you could use the following simple code:

my $out = sprintf( "%.2f", $_ ) =~ s/0+\z//r =~ s/\.\z//r;

Another way to look at it is that only two substrings can be removed: .00 at the end, or 0 at the end. This gives us the following solution:

my $out = sprintf( "%.2f", $_ ) =~ s/\.00\z|0\z//r;
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • ..thanks for the quick reply @ikegami !! It work, but is ther also a way to ignore the capture group all together if it wasnt captured in the first palce? – mcaustria Jul 03 '23 at 21:20
  • "Ignore a variable" is not a sentence that make sense. (The only time a programs will ignore a variable is if you don't use it.) Please clarify what you mean. – ikegami Jul 03 '23 at 22:38
  • See update. ___ – ikegami Jul 04 '23 at 00:13
  • as i mentioned in the beginning i wasnt looking for a more elegant way of solving my issue, more the possibilities of regex to handle the issue of an umdefined capturing group. I also like your solution but since its a perl only solution i gave this one to sin, Still thank you!! – mcaustria Jul 04 '23 at 13:46
  • 1
    Re "*its a perl only solution*", uh, so is the problem (the fact that using `$+{ext}` warns when undeifned). Again, any solution will be language specific. Because regex can't do anything. And you want something to happen. /// Re "*i gave this one to sin*", which doesn't set a default. He just provides a less elegant way of solving your problem. You just said you didn't want a *more* elegant solution that doesn't provide a default, so it's weird you accept a *less* elegant one that doesn't provide a default. – ikegami Jul 04 '23 at 21:27
  • @ikegami: Put a backslash before the period in `s/.00\z|0\z//r`? – Ed Sabol Jul 10 '23 at 10:03
  • 1
    @Ed Sabol, Thanks, fixed. – ikegami Jul 11 '23 at 01:28
2

If you insist on using Named groups for this...
Instead of using the e modifier, just define an empty ext in the alternation context.

use strict;
use warnings;

foreach(qw/2.1 2/){
    my $out= sprintf("%.2f", $_);
    $out =~ s/(?:(?<ext>\.[1-9]?)|(?<ext>)\.0)0([^\w]|$)/$+{'ext'}/g;
    print ":".$out.":\n";
}

Output

:2.1:
:2:

Even easier, do away with the named capture and use a Branch Reset.
Keep it portable in usage.

use strict;
use warnings;

foreach(qw/2.1 2/){
#foreach(@ARGV){
    my $out= sprintf("%.2f", $_);
    $out =~ s/(?|(\.[1-9]?)|()\.0)0([^\w]|$)/$1/g;
    print ":".$out.":\n";
}

Same output

:2.1:
:2:

See the accepted answer here for details: How to avoid warnings in Perl regex substitution with alternatives?

pilcrow
  • 56,591
  • 13
  • 94
  • 135
sln
  • 2,071
  • 1
  • 3
  • 11
  • Or `no warnings qw( uninitialized );` – ikegami Jul 03 '23 at 23:50
  • Oh, gone for all, or regex only ? Or turn on / off / on ? – sln Jul 03 '23 at 23:53
  • You can scope it to where you need. – ikegami Jul 04 '23 at 00:04
  • Yes, to me this is the best answer the branchreset and then the empty capturing group in the alternation - i didnt know empty capturing was possible , neither did i know you could use same named capturing groups...thanks a lot for the insite Chears!! – mcaustria Jul 04 '23 at 13:39
  • @mcaustria, I purposefully didn't include that in my answer because 1. It's not what you asked, and 2. It's a bad way of achieving what you want. My answer has much cleaner solutions. – ikegami Jul 04 '23 at 16:35