gunners.newark has asked for the wisdom of the Perl Monks concerning the following question:

Hi all, I'm pretty new to Perl. I'm trying to write a function which takes as input a 2D matrix of 0s and 1s(adjacency matrix) and changes a 0 value to 1 once where it finds a 0. The function is supposed to return the new matrix, without changing the old matrix which is passed as an argument to this function.
sub add_edge { my (@graph) = @_; my $node1 = int(rand(5)); my $node2 = int(rand(5)); print "Random node = $node1\n"; print "Random node = $node2\n"; my $i, $j; if (($node1 ne $node2) and ($graph[$node1][$node2] eq 0)) { $graph[$node1][$node2] = 1; return $graph; } for ($i = 0; $i < $graph_size; $i++) { for ($j = 0; $i < $graph_size; $j++) { if (($graph[$i][$j] eq 0) and ($i ne $j)) { $graph[$i][$j] = 1; return @graph; } } } }

Suppose my @G is a 5x5 matrix of all 0s.
@H = add_edge(@G);
If I write the above statement, I get the desired result in @H. But, it also modifies my original array @G, which I don't want to happen. So, what I'm seeking here is how to pass an array to a function and return as result another array which doesn't modify the original array.

Any suggestions would be highly appreciated.
Thanks.

Replies are listed 'Best First'.
Re: How to return a two dimensional array from a function in Perl?
by ikegami (Patriarch) on Nov 02, 2008 at 23:51 UTC

    without changing the old matrix which is passed as an argument to this function.

    There's no such thing as a 2d array in Perl. There are arrays whose elements are references to other arrays. That's what my (@graph) = @_; doesn't make a copy of the "2d array". Search for "deep copy"

    Other bugs:

    • Start by using use strict; and use warnings;. They will identify at least two errors.

    • eq and ne are string comparison operators, but you're using them to compare numbers. Use == and != to compare numbers.

    Other tips:

    • my $i; for ($i = 0; $i < $graph_size; $i++) {

      simplies to

      for (my $i = 0; $i < $graph_size; $i++) {

      and better yet

      for my $i (0..$graph_size-1) {
    • and and or are designed to be used to join statements. Use && and || inside of expressions.

      and and or are designed to be used to join statements. Use && and || inside of expressions.

      Huh? What practical difference is there between this:

      if (($graph[$i][$j] == 0) and ($i != $j))
      and this:
      if (($graph[$i][$j] == 0) && ($i != $j))
      apart from the fact the former is more English-like? Isn't each condition around the conjunction a kind of statement? (What exactly is the difference between a "statement" and an "expression"? -- actually, I'm not sure I want to get into that...)

        What practical difference is there between this:

        One practice is more likely to eventually burn you than the other.

        What exactly is the difference between a "statement" and an "expression"?

        I suppose it'd be safer to look at whether the program's flow is being controlled or not. or next, or return, or die, etc.

      and and or are designed to be used to join statements. Use && and || inside of expressions.

      Go on, I'll bite. Is there any matter of substance--like a practical difference in the results, or some hidden trap for the unwary--behind this statement?

      Or is it just another icky meme based on personel preference and groundless dogma.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

        Is there ... some hidden trap for the unwary--behind this statement?

        Yes, but it's not hidden. The precedence difference is documented. It comes up often enough, but I can't remember any examples.

        Or is it just another icky meme based on personel preference and groundless dogma.

        None of my style decisions are groundless.

      Thanks a lot for your suggestions ikegami.
        Hi,

        I am trying to do a simple return of a multidimensional array. I hoping to keep it simple, clean, so that I may use channel programming AI like ZPX Engine. $LON is a multidimensional array for this example. Don't care about the array reference at [0]. Want to return $LON and cleanly receive it into a target from the subroutine. [Left Hand] = [Right Hand] programming style. Basically an exact copy without re-processing it again. $a = $b; done.

        Super simple example below, did not even put in strict and warnings.

        Thank you in advanced.

        $ListPeople = (); $ListPeople = &ListOfNames; $counter = 0; while( $ListPeople[$counter][0] ne "" ){ print "$counter. What is my Name: $ListPeople[$counter][0] $ListPe +ople[$counter][1] $ListPeople[$counter][2]\n\n"; $counter++; }; sub ListOfNames{ $LON = (); $LON[0][0] = "Tim"; $LON[0][1] = "O"; $LON[0][2] = "Tay"; $LON[1][0] = "John"; $LON[1][1] = "Fitz"; $LON[1][2] = "Gerald"; $LON[2][0] = "Gerald"; $LON[2][1] = "Fitz"; $LON[2][2] = "John"; return( $LON ); }; exit(0);

        Output Should look like

        0. What is my Name: Tim O Tay

        1. What is my Name: John Fitz Gerald

        2. What is my Name: Gerald Fitz John

Re: How to return a two dimensional array from a function in Perl?
by graff (Chancellor) on Nov 03, 2008 at 03:51 UTC
    For the problem at hand, you could modify a copy of the 2-D array if you replace the initial line of your sub with these four lines:
    sub add_edge { my @graph; for ( @_ ) { push @graph, [ @$_ ]; } ...
    (But I would be inclined to add some checks, and Carp if the input list is empty or includes anything that isn't an array ref.)
      Thanks a lot graff. That really helped.
Re: How to return a two dimensional array from a function in Perl?
by hda (Chaplain) on Nov 03, 2008 at 09:28 UTC
    If what you need is efficient use of 2D matrices you could try using the PDL module, the Perl Data language:

    http://pdl.perl.org/

Re: How to return a two dimensional array from a function in Perl?
by harangzsolt33 (Deacon) on Nov 05, 2018 at 03:20 UTC

    Suppose my @G is a 5x5 matrix of all 0s.

    Okay, if you are going to use a fixed-length matrix, then you could use a string and pretend that the bits in the string are the individual elements in the arrays. Use vec() to manipulate the string. The advantage of this is that creating a string will take less memory than creating an array. Also, you will be able to return the 2D matrix from the function call, because it's a simple string.

    Here is an example: $a and $b are going to be the pointers in the matrix. So, instead of doing

    my $VALUE = $MyMatrix[$a][$b];
    you are going to do this instead:
    my $VALUE = (vec($MyMatrix, ($a & 7), 8) >> $b) & 1;
    Here we are assuming that your matrix has 8 elements in each row, since there are 8 bits in a byte. You can also use vec() to overwrite individual bits in the matrix. I'd write a sub that either reads from or writes to the matrix. That way you don't have to write all these calculations every time you want to address the various elements in the matrix. Your 5x5 matrix would take up 5 bytes of space in memory. So, this would be the 2D "array" that your function would return. It would be a string.

    Here is a working example:

    #!/usr/bin/perl -w use strict; use warnings; my $i; my $j; # Create our test matrix my $MyMatrix = NewMatrix(); # Shows that all matrix values are initially ZERO. DisplayMatrix(); # Set all matrix values to ONE. for ($i = 0; $i < 5; $i++) { for ($j = 0; $j < 5; $j++) { $MyMatrix = SetMatrixValue($MyMatrix, $i, $j, 1); } } DisplayMatrix(); # Clear somes random values... $MyMatrix = SetMatrixValue($MyMatrix, 1, 4, 0); $MyMatrix = SetMatrixValue($MyMatrix, 2, 0, 0); $MyMatrix = SetMatrixValue($MyMatrix, 2, 3, 0); DisplayMatrix(); # Flip bits from zero to one and one to zero. $MyMatrix = NOT_Matrix($MyMatrix); DisplayMatrix(); exit; # # This function reads a bit from the 5x5 matrix. # Returns either 1 or 0. # Usage: GetMatrixValue(MATRIX_STRING, X_POS, Y_POS) # sub GetMatrixValue { my $MATRIX = GetMatrixStr(shift); my $X = (shift) & 7; my $Y = (shift) & 7; # Make sure the pointers are within the right range. $X <= 4 or $X = 4; $Y <= 4 or $Y = 4; return (vec($MATRIX, $Y, 8) >> $X) & 1; } # # This function changes a bit in the 5x5 matrix string. # Returns the new matrix string. # Usage: NEW_MATRIX_STRING = SetMatrixValue(MATRIX_STRING, X_POS, Y_PO +S, NEW_VALUE) # sub SetMatrixValue { my $MATRIX = GetMatrixStr(shift); my $X = (shift) & 7; my $Y = (shift) & 7; my $NEW_VALUE = (shift) & 1; # Make sure the pointers are within the right range. $X <= 4 or $X = 4; $Y <= 4 or $Y = 4; vec($MATRIX, ($Y << 3) + $X, 1) = $NEW_VALUE; return $MATRIX; } # # This function makes sure that the matrix # string is exactly 5 bytes long. # Usage: MATRIX_STRING = GetMatrixStr(MATRIX_STRING) # sub GetMatrixStr { my $Z = "\0" x 5; @_ or return $Z; defined $_[0] or return $Z; return (length($_[0]) == 5) ? $_[0] : substr("$_[0]$Z", 0, 5); } # # This function performs bitwise NOT operation on all the # bits of the matrix and returns the matrix string. # Usage: MATRIX_STRING = NOT_Matrix(MATRIX_STRING) # sub NOT_Matrix { my $MATRIX = GetMatrixStr(shift); for (my $i = 0; $i < 5; $i++) { vec($MATRIX, $i, 8) = ~vec($MATRIX, $i, 8); } return $MATRIX; } sub NewMatrix { return "\0" x 5; } sub DisplayMatrix { my $j; print "\n\n--- MATRIX " . ('-' x 60) . "\n\n"; for (my $i = 0; $i < 5; $i++) { for ($j = 0; $j < 5; $j++) { print "\t[$i,$j]=" . GetMatrixValue($MyMatrix, $i, $j); } print "\n"; } PAUSE(); } sub PAUSE { $| = 1; print "\nPRESS ENTER TO CONTINUE...\n"; <STDIN>; return 0; }
Re: How to return a two dimensional array from a function in Perl?
by RedRiverRun (Initiate) on Nov 05, 2018 at 00:00 UTC
    Thank you, I see what I was doing wrong. Based on the example you submitted. $buildit[0][0]="Oh";, return( @buildit ); @RecvIt = &Shazam; print "it:$RecvIt[0][0]\n"; Just have to remember to marshal your @ and $ in the code. [left hand[swap @:$]] and [right hand[swap $:@]], perfect, thank you.

    2018-11-05 Athanasius added code tags