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

I'm hoping someone has a clever way to do this...

Okay, I have an array of arrays (which can just as easily be an array of strings, just don't split the strings into individual characters) and want to perform substitutions vertically. i.e., given this matrix:
xxxYYxxxYYxxx YY YY 4Y 4X 4Y 4Y YY YY
I'd like to do two things, first of all, change all Y's horizontally between x's into x's themselves, and change all 4's vertically between Y's into Y's themselves. This is the result:
xxxxxxxxxxxxx YY YY YY YX YY YY YY YY
Now, the horizontal match to turn the Y's into x's is easy, just join on the line and do the regex as normal. But what's a nice, clever way to do the vertical match to turn the 4's into Y's? (don't worry about precedence compared to the horizontal match)

I know I can always transpose the matrix, do the now horizontal substitution, and then transpose it back, but that seems like an awful lot of effort. Anyone know a smarter way to do it?

Replies are listed 'Best First'.
Re: vertical regex in a matrix?
by chromatic (Archbishop) on Sep 11, 2000 at 04:34 UTC
    A regex isn't the best way to handle this. (In fact, if you could get one working, I'd be really impressed.)

    You'll probably have better luck with PDL, which is designed to give you a nice Perl interface to powerful mathematical constructs, like matrices. Someone like tilly might have used it before.

      Fully agree, but even more directly and also easy with Math::Matrix.
RE: vertical regex in a matrix?
by BooK (Curate) on Sep 11, 2000 at 18:36 UTC

    When you want do things such as working with multiline strings that represent a board (or a matrix or whatever), the best way is to unfold the string.

    Either by removing the \n, and printing the line with something like:
    s/(.{$L})/$1\n/sg where $L is the wanted length; or by putting the whole board in a single string and using the \n as line delimiters.

    Here I use the latter solution. You can use two regex, if your input is indeed a matrix (i.e. all lines are of the same length). First you can work the 'Y' columns, and then the 'x' line:

    # replace whatever is at columns 3,4, 8 and 9 by 'Y' s/^(...)..(...)..(...)$/$1YY$2YY$3/mg; # set the first line to 'xx' s/\G(x*)./$1x/gm;

    There are more than one way to do this. What is it that you specifically want to do? Replace all vertical 4's by Y's or what?

    Tips: /m let ^ and $ match the beginning and the ending of a line (on a multiline string). \A and \Z match the beginnning and ending of the string. I also used the fact that . doesn't match a newline, so the second regex cannot match further than the first line.

    I am not really sure it's smarter than transposition, though...

Re: vertical regex in a matrix?
by japhy (Canon) on Sep 23, 2000 at 07:02 UTC
    I wrote a program, named 'proximity', that does matrix-type things (2-D stuff, basically) with regexes.
    #!/usr/bin/perl $string = << "END"; ABC DEF GHI END for $a (0 .. 2){ for $b (0 .. 2) { ($u,$l,$P,$r,$d) = prox($string, $a, $b); print "$P: u=$u, l=$l, r=$r, d=$d\n"; } } sub prox { my ($str,$r,$c) = @_; my $rlen = index($str, "\n"); my $skip = '.' x (($rlen + 1) * $r + $c); my $b = '.' x $rlen; my $f = '.' x $rlen; return $str =~ m{ ^ (?s:$skip) (?s:(?<=(.)$b)?) (?<=(.))? (.) (?=(.))? (?s:(?=$f(.))?) }x; }
    As for variable-length look-behinds... you can implement those via revexes!

    $_="goto+F.print+chop;\n=yhpaj";F1:eval
RE: vertical regex in a matrix?
by ncw (Friar) on Sep 12, 2000 at 00:41 UTC
    This was my attempt
    my $string = <<EOF; xxxYYxxxYYxxx YY YY 4Y 4X 4Y 4Y YY YY EOF print "1: $string"; $string =~ s/ (?: (?<=x) | \G ) (Y) (?= Y* x) /x/gx; print "2: $string"; $string =~ s/ (?<= ^ (.*) Y .* $ ^ \1 ) (4) (?= .* $ ^ \1 Y .* $ ) /Y/mgx; print "3: $string";
    The first part to match the horizontal parts works fine. The second part to match the verticals doens't work though for two reasong
    1. It is going to match the same string horizontally in each case up to the vertical replacement (which wasn't specified)
    2. Perl gave this message "variable length lookbehind not implemented" when I tried it ;-)