XP is just a number PerlMonks

### Tips on how to perform this substitution?

 on Jan 22, 2014 at 13:06 UTC Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,
so, I am new to Perl and currently trying to play around with some small tasks in order to improve my skills...
I am now learning regular expressions and I am stuck with this issue:
Imagine that you have a line like the following:
```------BBBBB-----------------------------------------------------------
+-------------------------------------------------BBBBBB----BBBBBBBB--
+---------------BBBBBB------------------------------------------------
+--------BBBBBBB---------------BBBBB-----BBBBBBBBB--------------------
+----------------BBBBBBBBB------BBBBBBBBB----------------------------B
+BBBBBBBB--------------------------------------BBBBBBBB-------BBBBBB--
+-------------------BBBBBBB--------------------------BBBBBBBB----

and you want to transform it to:
```iiiiiiMMMMMooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
+oooooooooooooooooooooooooooooooooooooooooooooooooMMMMMMiiiiMMMMMMMMoo
+oooooooooooooooMMMMMMiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+iiiiiiiiMMMMMMMoooooooooooooooMMMMMiiiiiMMMMMMMMMoooooooooooooooooooo
+ooooooooooooooooMMMMMMMMMiiiiiiMMMMMMMMMooooooooooooooooooooooooooooM
+MMMMMMMMiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiMMMMMMMMoooooooMMMMMMii
+iiiiiiiiiiiiiiiiiiiMMMMMMMooooooooooooooooooooooooooMMMMMMMMiiii

, which, in practical terms means: replace all B's with M's and, as far as the - are concerned, start with i and then alternate between i and o on the two sides of the M parts. The B -> M replacement of course is trivial to do and I could also say that if you find ---- in the beginning of the line, replace them with iiii...But I am not sure how to do the alternation thing!
Can you help me on this a bit?

Replies are listed 'Best First'.
Re: Tips on how to perform this substitution?
by moritz (Cardinal) on Jan 22, 2014 at 13:30 UTC

The trick with the alternation is that your regex matches -s delimited by B's, thus slurping them up. The next regex search starts off after the last B, and needs to skip the following -s to find the next B.

```use 5.010;
use strict;
use warnings;

\$_ = '------BBBBB-----------------------------------------------------
+-------------------------------------------------------BBBBBB----BBBB
+BBBB-----------------BBBBBB------------------------------------------
+--------------BBBBBBB---------------BBBBB-----BBBBBBBBB--------------
+----------------------BBBBBBBBB------BBBBBBBBB-----------------------
+-----BBBBBBBBB--------------------------------------BBBBBBBB-------BB
+BBBB---------------------BBBBBBB--------------------------BBBBBBBB---
+-';

my \$expected = 'iiiiiiMMMMMooooooooooooooooooooooooooooooooooooooooooo
+oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooMMMM
+MMiiiiMMMMMMMMoooooooooooooooooMMMMMMiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+iiiiiiiiiiiiiiiiiiiiiiiiMMMMMMMoooooooooooooooMMMMMiiiiiMMMMMMMMMoooo
+ooooooooooooooooooooooooooooooooMMMMMMMMMiiiiiiMMMMMMMMMooooooooooooo
+oooooooooooooooMMMMMMMMMiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiMMMMMMM
+MoooooooMMMMMMiiiiiiiiiiiiiiiiiiiiiMMMMMMMooooooooooooooooooooooooooM
+MMMMMMMiiii';

s/(^|B+)(-+)(\$|B+)/ \$1 . ("i" x length(\$2)) . \$3/eg;
s/-/o/g;
s/B/M/g;

say \$expected;
say \$_;

say \$expected eq \$_ ? 'yes' : 'no';

It can be done in one s/// operation...

```use v5.10;
use strict;
use warnings;

\$_ = '--BBB----BB--B-------B--B--BBBB---B--';

s ((B+|.)) {
state \$char = 'i';
if (index(\$1, 'B') == 0) {
\$char = \$char eq 'i' ? 'o' : 'i';
'M' x length(\$1);
}
else {
\$char;
}
}eg;

say;
use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name

That is way too complex :)

```my \$p = "--BBB----BB--B-------B--B--BBBB---B--";

my @x = qw( i o );
my \$x = 0;

\$p =~ y/B/M/;
\$p =~ s/(-+)/\$x[\$x++%2]x length(\$1)/ge;

say \$p;

Enjoy, Have FUN! H.Merijn
Thank you very much for your kind tips!
Will look at them carefully!
Re: Tips on how to perform this substitution?
by kcott (Chancellor) on Jan 22, 2014 at 14:59 UTC

Here's another way to do it:

```#!/usr/bin/env perl -l

use strict;
use warnings;

my \$string = '---BB--BBB---BBB---B-B---';
print \$string;

\$string =~ y/B/M/;
print \$string;

my @subs = qw{i o};
my \$i = 1;
\$string =~ s/(-+)/\$subs[\$i ^= 1] x length \$1/eg;
print \$string;

Output:

```---BB--BBB---BBB---B-B---
---MM--MMM---MMM---M-M---
iiiMMooMMMiiiMMMoooMiMooo

-- Ken

Re: Tips on how to perform this substitution?
by hdb (Prior) on Jan 22, 2014 at 13:26 UTC
Re: Tips on how to perform this substitution?
by TomDLux (Vicar) on Jan 23, 2014 at 05:21 UTC

Rather than focusing on how to do this in as few characters as possible, consider doing it in a manner that is clear

```my \$str = '---BBB--- ..... ';
my @bits = split /(B+)/, \$str;

produces @bits:

```0: '---'
1: 'BBB'
2:'---'

processing through @bits, you have to consider the possibility that the first character was a 'B', in which case \$bits[0] will be B', in which case you convert to M's, otherwise the first part will convert to the odd pattern. Then a string of Bs, then the even pattern, and so on.

As Occam said: Entia non sunt multiplicanda praeter necessitatem.

Re: Tips on how to perform this substitution?
by Anonymous Monk on Jan 22, 2014 at 14:05 UTC
Another useful thing to do here is to use the g and c modifiers. From perldoc perlre:
g and c
Global matching, and keep the Current position after failed matching. Unlike i, m, s and x, these two flags affect the way the regex is used rather than the regex itself. See "Using regular expressions in Perl" in perlretut for further explanation of the g and c modifiers.
The difference here is that, when you use these two modifiers together, you can loop across the string repeatedly (an internal cursor advances down the string), and you can try different things to see if any of them match, e.g. in an if..elsif structure. No, this is not minimalist; it is not "golf." But it can come in handy in situations like this, where a single regular-expression might not be easy to construct and verify, but a short loop and if-statements are clear.
Re: Tips on how to perform this substitution?
by hdb (Prior) on Jan 22, 2014 at 15:04 UTC

Two more ways to flip between 'i' and 'o':

```\$char = 'i';

\$char = chr 216 - ord \$char;
\$char =~ tr/io/oi/;
Re: Tips on how to perform this substitution?
by Anonymous Monk on Jan 23, 2014 at 00:51 UTC

Golf:

```\$_ = '--BBB----BB--B-------B--B--BBBB---B--';

s/-+/--\$|x\$&=~y---c/ge;y/B10/Mio/;

say;

Out:

```iiMMMooooMMiiMoooooooMiiMooMMMMiiiMoo

Create A New User
Node Status?
node history
Node Type: perlquestion [id://1071606]
Approved by hdb
Front-paged by Old_Gray_Bear
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (4)
As of 2017-06-24 21:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
How many monitors do you use while coding?

Results (562 votes). Check out past polls.