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

I have a section of code which interprets alpha-numeric codes such as 1A2B3C3B1D:

my %rules = (A => 1,B => 1,C => 0,D => 1); my $s = 0; while ($F[2] =~ /(\d+)([ABCD])/g) { my $n = $1; my $op = $2; $s += $n * $rules{$op}; }

However, I now wish to calculate different versions of $s based on the position of 'D' in the alpha-numeric code. D can only ever appear as the first or last letter in the code (or both). How could I alter the code such that I can create the following variables?

$s - Values from D at both ends are added to the total - if D does not + exist at both ends in any given code, whichever D is present is adde +d regardless (the way the code currently works) $s1 - Only the value from D at the start of the code is added $s2 - Only the value from D at the end of the code is added

So as an example, the code 2D40A2C1B4D would produce the following values:

$s = 47 (2+40+1+4) $s1 = 43 (2+40+1) $s2 = 45 (40+1+4)

And 2D40A2C1B would produce:

$s = 43 (2+40+1) $s1 = 43 (2+40+1) $s2 = 41 (40+1)

Replies are listed 'Best First'.
Re: Detecting position of a substring within a string
by Anonymous Monk on Mar 20, 2016 at 15:42 UTC

    One possible solution is to check @- and @+:

    use warnings; use strict; sub calcit { my $in = shift; my %rules = (A => 1,B => 1,C => 0,D => 1); my ($s,$s1,$s2) = (0)x3; while ($in=~/(\d+)([ABCD])/g) { my ($n,$op) = ($1,$2); $s += $n * $rules{$op}; $s1 += $n * $rules{$op} unless $op eq 'D' && $-[0]>0; $s2 += $n * $rules{$op} unless $op eq 'D' && $+[0]<length($in); } return ($s,$s1,$s2); } use Test::More; is_deeply [calcit("2D40A2C1B4D")], [47,43,45], "2D40A2C1B4D"; is_deeply [calcit("2D40A2C1B")], [43,43,41], "2D40A2C1B"; done_testing;

    But TIMTOWTDI, for example you could ignore all "D" codes in the initial processing of the string and then use a separate regex (/^(\d+)D/ resp. /(\d+)D$/) to account for them.

      Thank you! I've managed to successfully modify this to fit into my script and the desired result is met.
Re: Detecting position of a substring within a string
by graff (Chancellor) on Mar 20, 2016 at 20:55 UTC
    Since the matter hinges on knowing what the first and last elements of the string are, I'd be inclined to start by splitting the string into an array, which makes it easy to treat the first and last directly. A capturing split would do it well enough:
    #!/usr/bin/perl use strict; use warnings; my %rules = (A => 1,B => 1,C => 0,D => 1); while (<DATA>) { chomp; my @chunks = split( /([ABCD])/ ); my $frst_add = my $last_add = 0; if ( $chunks[1] eq 'D' ) { $frst_add = $chunks[0] * $rules{D}; shift @chunks; shift @chunks; } if ( $chunks[-1] eq 'D' ) { $last_add = $chunks[-2] * $rules{D}; pop @chunks; pop @chunks; } my $s = 0; for ( my $i = 1; $i < @chunks; $i += 2 ) { $s += $chunks[$i-1] * $rules{$chunks[$i]}; } my $s1 = $s + $frst_add; my $s2 = $s + $last_add; $s += $frst_add + $last_add; printf "%s :\ts= %2d s1= %2d s2= %2d\n", $_, $s, $s1, $s2; } __DATA__ 1A2B3C3B1D 2D40A2C1B4D 2D40A2C1B
    (Of course, having written this as a stand-alone script, I think it would be better as a subroutine, like what AM did above.)