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

Been banging away at this now for HOURS, but again I am too stupid and must yield to the superior intellects..

I want to take a string and flip chars 1&2, then 2&3, etc. I got it where I could flip 1&2, then 3&4, but that isn't quite it. I keep running into this one problem and I can't figure out why. Here is my code (sad but true):

for (@words) { while(/(.)/g) { $flip = $_; $flip =~ s/$1\./\.$1/ge; # flip match + next char print $flip; } }
I am getting various errors, but nothing helpful in spite of using "perl -w". I am using the $flip var so I don't change the value of $_ ( I want to do some more stuff later on with it, and I want to stay the same throughout.. probably a better way to do that too, but we only know what we know). Can I not call $1 in my search and replace?? It never seems to work right. It does seem to work if I copy $1 to another var and then use that in my regexp.. I really need to sit down with my "Mastering Regular Expressions" book and quit pestering you guys. =)

So, the output of say "jigglewort" is:

ijgglewort
jgiglewort
jigglewort
jiglgewort
...

The current error message is:
syntax error at flipchar.pl line 19, near "\."

Replies are listed 'Best First'.
Re: YET another regexp puzzle
by Coruscate (Sexton) on Nov 20, 2003 at 02:35 UTC

    This is an obvious candidate for substr(). The following code just takes the substring of every pair of sequential characters and replaces each set with the reverse of that substring, effectively swapping the 2 characters.

    #!c:/perl/bin/perl -w $|++; use strict; my $word = 'jigglewort'; for my $i (0 .. length($word)-1) { my $tmp = $word; substr($tmp, $i, 2, reverse substr($tmp, $i, 2)); # _or_ # substr($tmp, $i, 2) = reverse substr($tmp, $i, 2); print $tmp, "\n"; } =for output ijgglewort jgiglewort jigglewort jiglgewort jiggelwort jigglweort jiggleowrt jigglewrot jigglewotr jigglewort =cut

    Update: Added lvalue substr as an option :)

      SWEET!

      That was the ticket! I really appreciate that.. now my brain can quit hurting, and I'll add that to my tool kit.
Re: YET another regexp puzzle
by Roger (Parson) on Nov 20, 2003 at 03:11 UTC
    Have you thought about doing this with a slice?

    my $str = "jigglewort"; my @a = split //, $str; print join('',@a[0..$_-1, $_+1, $_, $_+2..$#a]),"\n" for 0..$#a;
    And the output is as expected -
    ijgglewort jgiglewort jigglewort jiglgewort jiggelwort jigglweort jiggleowrt jigglewrot jigglewotr jigglewort
      Excellent! That looks like it would kick it too.
      THANKS!!
Re: YET another regexp puzzle
by Abigail-II (Bishop) on Nov 20, 2003 at 10:41 UTC
    Sounds like a job for Mr. Pos.
    #!/usr/bin/perl use strict; use warnings; my $w = 'jigglewort'; do {my $t = $w; pos ($t) = $_; $t =~ s/\G(.)(.)/$2$1/; print "$t\n"} for 0 .. length ($w) - 2; __END__ ijgglewort jgiglewort jigglewort jiglgewort jiggelwort jigglweort jiggleowrt jigglewrot jigglewotr

    Abigail

Re: YET another regexp puzzle
by duff (Parson) on Nov 20, 2003 at 04:48 UTC

    I read through the responses so far and figured that it might be useful to someone to examine how someone might solve this problem using regular expressions (since that's how the problem was originally framed), even though I'd probably do something with substr() myself. Here are my thought processes.

    First I thought, "how would I do this manually?" and I came up with the following REs

    s/^(.)(.)(.*)/$2$1$3/s; # 12345 -> 21345 s/^(.)(.)(.)(.*)/$1$3$2$4/s; # 12345 -> 13245 s/^(..)(.)(.)(.*)/$1$3$2$4/s; # 12345 -> 12435 s/^(...)(.)(.)(.*)/$1$3$2$4/s; # 12345 -> 12354

    The "(.)(.)" part matches the two characters we're going to swap and the "(.*)" part matches whatever is left in the string. I put the /s modifier on there in case a linefeed character was in the string.

    Then I got to thinking ... it sure would be nice if that first s/// looked like all the others. So, of course, I got

    s/^()(.)(.)(.*)/$1$3$2$4/s;

    So now how do I put that stuff in a loop? One of the many cool things in perl is that you can interpolate your patterns like so:

    my $dots = ".."; s/^($dots)(.)(.)(.*)/$1$3$2$4/s; # 12345 -> 12435

    So, all I need to do is loop until length($dots) is greater than length($str)-2 (the -2 is because of the pair of characters we are swapping). So that gives me this final bit of code:

    my $str = "12345"; my $dots = ""; while (length($dots) <= length($str)-2) { $_ = $str; s/^($dots)(.)(.)(.*)/$1$3$2$4/s; print; $dots .= "."; }

    Hope this helps,

Re: YET another regexp puzzle
by Coruscate (Sexton) on Nov 20, 2003 at 05:20 UTC

    Since this is so much fun, let's do one that uses both the regex and the substr :)

    my $word = 'jigglewort'; print $word, "\n"; while ($word =~ /(.)(.)/g) { my $tmp = $word; substr($tmp, pos($word)-2, 2) = "$2$1"; print $tmp, "\n"; pos($word) = pos($word) - 1; } =for output jigglewort ijgglewort jgiglewort jigglewort jiglgewort jiggelwort jigglweort jiggleowrt jigglewrot jigglewotr =cut

Re: YET another regexp puzzle
by Anonymous Monk on Nov 20, 2003 at 01:50 UTC
    Like this?
    my $c = 0; $_ = "jigglewort\n"; print and $c++ while s/(.{$c})(.)(.)/$1$3$2/;
    producing:
    ijgglewort igjglewort iggjlewort iggljewort igglejwort igglewjort igglewojrt iggleworjt igglewortj
      I didn't want to "move" the first char across the string. I want to swap 1&2, 2&3, 3&4. This way:

      12345
      21345
      13245
      12435
      12354

      Thanks though.. your idea is cool. EVERY response helps me learn a little more even if it's not exactly what I was after.