Linear interpolation of missing data from table of values - arrays

Here is an excerpt from a data file, my starting point:
Marker Distance_1 Distance_2 ID
.
.
.
30 13387412 34.80391242 seq-SN_FIRST
31 13387444 34.80391444 seq-SN_Second
31.1
31.2
32 13387555 39.80391 seq-SN_Third
.
.
.
This is a tab-delimited file of multiple rows of four elements each. First row is a header. After that, numerous rows of data. The vertical dots are not actually in the real file, but they are here just to represent that data similar to the actual rows shown occur before and after the example fo rows explicitly shown.
Some of the data rows are "full", that is, all four cell entries contain something. Other rows are "blank", with only a first actual entry but followed by 3 tab delimited single spaces. Those white spaces in the blank rows need to be "filled in." The filling in will be done by linear interpolation, using the corresponding cell entries of the immediately preceding and immediately succeeding rows. For example, missing Distance_1 values, in column 2, will be interpolated using the value 13387444 of the preceding row and the value 13387555 of the succeeding row. Similarly so for the column 3 values. Column 4 values are just ignored here.
The first goal of the script is to identify the blocks of data that require filling and their flanking "full" lines. Blank lines will contain 3 tabbed single spaces and will be ID'd that way. Once found, the consecutive sets of blank lines plus flanking full lines are sent to subroutine for interpolation.
#!/usr/bin/perl
use strict;
use warnings;
die "usage: [ map positions file post SAS ]\n\n" unless #ARGV == 1;
my #file = ();
while (my $line = <$ARGV[0]>) {
chomp $line;
push(#file, $line);
}
my #consecutive_lines = (); # array collects a current set of consecutive lines requiring linear interpolation
my #filled = (); # my final array, hopefully correctly filled in
#####
# search for consecutive set of lines in #file
#####
for (my $i = 0; $i < $#file; $i++) { # $#file returns the index of the last element in #file
if ($file[$i] !~ /(?:\t\s){3}/) { # if not a blank line
# but a "full line"
push(#filled, $file[$i]); # push the header and full lines, until...
}
elsif ($file[$i] =~ /(?:\t\s){3}/) { # ...a blank line is found
push (#consecutive_lines, $file[$i - 1]); # push preceding full line into array
while ($file[$i] =~ /(?:\t\s){3}/ and $i < $#file) { # keep pushing lines, so long as they are blank
# or end of file
push(#consecutive_lines, $file[$i++]);
}
push(#consecutive_lines, $file[$i]) ; # else we reach next full line, so push it into array
my #fillme = linearInterpolation(#consecutive_lines); # send set of lines out for filling
push(#filled, #fillme); # push filled in set of lines into the final array
#consecutive_lines = (); # reset or undef array #consecutive_lines for next cycle
} # end of elsif
} # end of for loop
Thanks to user #Kenosis for lots help with the above, which I have since modified (hopefully not mangled).
Next is the linear interpolation. It is here where I am trying to link the first phase of the script to the second phase. And it is not working well so far.
My goal is to hand off the array #incoming to the subroutine. This array is then split up, so that the actual cell entries are "visible" and can be indexed by the array, and so called upon. I have been trying to figure out how to do this for the column 2 values for Distance_1 first. I feel like this script gets close and it begins to stray at the point just after the interpolated values are calculated.
#####
# subroutine linear interpolation
#####
sub linearInterpolation {
my #incoming = #_; # array of consecutive set of lines
my #splitup; # declare new array, will be a "split up" version of #incoming
my ($A, $B, $C, $D, $E); # variables for linear interpolation
my #fillme; # declaring the "emtpy" array to be filled in
my #dist_1_fills; # array of interpolated values for dist_1
for (my $i = 0;
$i < scalar #incoming; $i++) # loop to split up lines of #incoming
{ # into indexed cell entries
chomp $incoming[$i]; # and make new array of them
my #entries = split('\t', $incoming[$i]);
push(#splitup, #entries);
}
$A = $splitup[1]; # cell entry in column 2 of preceding full line
$B = $splitup[-3]; # cell entry in column 2 of succeeding full line
$C = $splitup[2]; # cell entry in column 3 of preceding full line
$D = $splitup[-2]; # cell entry in column 3 of succeeding full line
$E = scalar #incoming - 1; # equals number of lines in the set minus 1
for (my $i = 1; $i < $E; $i++) { # need to start finding appropriate
# number interpolated values, given number of
my #dist_1_fills =
interpvalues($A, $B, $E, $i); # of lines in consecutive set of lines
for ($i = 0; $i < scalar #splitup; $i += 4) {
push(#fillme, $splitup[$i], $dist_1_fills[$i], "dist_2_fills", "--");
# fourth column values will be ignored or filled with --.
# "dist_2_fills" just occupying it's proper spot until I can figure out distance 1 fills
}
}
}
#########
sub interpvalues { # subroutine to find interpolated values
my ($A, $B, $E, $i) = #_;
my $dist_1_answers = (($B - $A) / ($E)) * $i + $A;
return $dist_1_answers;
}
The code gets confused in the second part that deals with finding the interpolated values and sending them back to the first part of the code to finally fill in the data set. I think specifically my biggest (though probably not my only) problem is trying to fill in the blank lines with the proper values after they have been calculated in the second subroutine.
Any hints and clues are greatly appreciated!

This program will do what you need. It expects the inout file name as a parameter on the command line.
use strict;
use warnings;
my #saved;
my #needed;
while (<>) {
chomp;
my #fields = split /\t/;
# Pass hrough headers and junk
unless ($fields[0] and $fields[0] =~ /\d/) {
print "$_\n";
next;
}
# Save x-value for records without a y-value
if ($fields[1] !~ /\d/) {
push #needed, $fields[0];
next;
}
# We have a filled-out row. Calculate any intermediate missing ones
if (#needed) {
if ($saved[0] == $fields[0]) {
die sprintf qq(Duplicate marker values %.1f at line %d of "%s"\n), $saved[0], $., $ARGV;
}
my ($a1, $b1) = solve_linear(#saved[0,1], #fields[0,1]);
my ($a2, $b2) = solve_linear(#saved[0,2], #fields[0,2]);
while (#needed) {
my $x = shift #needed;
my $y1 = $a1 * $x + $b1;
my $y2 = $a2 * $x + $b2;
print join("\t", $x, $y1, $y2), "\n";
}
}
print "$_\n";
#saved = #fields;
}
sub solve_linear {
my ($x0, $y0, $x1, $y1) = #_;
my ($dx, $dy) = ($x1 - $x0, $y1 - $y0);
my $aa = $dy / $dx;
my $bb = ($y0 * $dx - $x0 * $dy) / $dx;
return ($aa, $bb);
}
output
Marker Distance_1 Distance_2 ID
.
.
.
30 13387412 34.80391242 seq-SN_FIRST
31 13387444 34.80391444 seq-SN_Second
31.1 13387455.1 35.303913996 --
31.2 13387466.2 35.803913552 --
32 13387555 39.80391 seq-SN_Third
.
.
.
Tool completed successfully

I modified the code to this, so that the linear interpolation is based not on the values in the first column but rather on the values in the second and third columns. Thanks especially to users #Kenosis and #Borodin. I have accepted Kenosis' answer to a previous question, and I have accepted Borodin's here, even though I post this revision in the "answer your own question" section. Is posting a revision here acceptable? I skimmed the FAQ about this but haven't found anything relevant yet.
#!/usr/bin/perl
use strict; use warnings;
my #saved;
my #needed;
while (<>) {
chomp;
my #fields = split /\t/;
# Does the current line actually exist AND does it contain one or more digits.
unless ($fields[0] and $fields[0] =~ /\d/) {
# If no, this is the header, so print it. If yes, advance.
print "$_\n";
#after printing header, go back to <> and read in next line.
next;
}
# Is the second cell of the current line devoid of digits?
if ($fields[1] !~ /\d/) {
# If no, advance. If yes, remember $field[0], the Marker.
push #needed, $fields[0];
# After pushing, go back to <> and read in next line.
next;
}
# If we are here, we must have a filled-out row.
# Does #needed have any values? If no, advance. If yes,
if (#needed) {
if ($saved[0] == $fields[0]) {
die sprintf qq(Duplicate marker values %.1f at line %d of "%s"\n), $saved[0], $., $ARGV;
}
# Else send preceding dist_1 value, succeeding dist_1 value,
# preceding dist_2 value, succeeding dist_2 value,
# and number of emtpy lines to subroutine.
my ($dist_1_interval, $dist_2_interval) = interval_sizes($saved[1], $fields[1], $saved[2], $fields[2], scalar #needed);
# Current size of #needed is saved as $size and is used to help with iteration.
# So long as #needed contains values...
my $size = scalar #needed;
while (#needed) {
# ...remove left-most Marker value from array #needed.
my $x = shift #needed;
# Interpolated values for dist_1 and dist_2 are
# (respective interval size x iteration of while loop) + preceding values.
my $new_dist_1 = ($dist_1_interval * (1 + ($size - (scalar #needed + 1)))) + $saved[1];
my $new_dist_2 = ($dist_2_interval * (1 + ($size - (scalar #needed + 1)))) + $saved[2];
print join("\t", $x, $new_dist_1, $new_dist_2, "--"), "\n";
}
}
# We are here since current line is already a filled-in row.
print "$_\n";
# Print this row and assign it to #saved. Return to <>.
#saved = #fields;
}
sub interval_sizes {
# $A = preceding dist_1, $B = succeeding dist_1,
# $C = preceding dist_2, $D = succeeding dist_2,
# $E = number of needed distances.
my ($A, $B, $C, $D, $E) = #_;
# I need an interval size for dist_1 based on difference between $B and $A.
my $dist_1_interval = ($B - $A)/($E + 1);
# I need an interval size for dist_2 based on difference between $D and $C.
my $dist_2_interval = ($D - $C)/($E + 1);
return ($dist_1_interval, $dist_2_interval);
}

Related

Incorporate year matching sub-routine into script and apply condition to result

I have a script which reads a csv file line by line, and compares the title in field 2 of to another csv file. If 5 or more words match, the it prints out the line of each file which matches this criteria. Here is the script:
#!/bin/perl
#subroutine for discovering year
sub find_year {
my( $str ) = #_;
my $year = $1 if( $str =~ /\b((?:19|20)\d\d)\b/ );
return $year
}
#####CREATE CSV2 DATA
my #csv2 = ();
open CSV2, "<csv2" or die;
#csv2=<CSV2>;
close CSV2;
my %csv2hash = ();
my #csv2years;
for ( #csv2 ) {
chomp;
my ($title) = $_ =~ /^.+?,\s*([^,]+?),/; #/define the data which is the title
$csv2hash{$_} = $title; # Indicate that title data will input into csv2hash.
}
###### CREATE CSV1 DATA
open CSV1, "<csv1" or die;
while (<CSV1>) {
chomp; #removes new lines
my ($title) = $_ =~ /^.+?,\s*([^,]+?),/; #/ creates variable of title
my %words;
$words{$_}++ for split /\s+/, $title; #/ get words
## Collect unique words into an array- the # means an array
my #titlewords = keys(%words);
# Add exception words which shouldn't be matched.
my #new;
foreach my $t (#titlewords){
push(#new, $t) if $t !~ /^(rare|vol|volume|issue|double|magazine|mag)$/i;
}
###### The comparison algorithm
#titlewords = #new;
my $desired = 5; # Desired matching number of words
my $matched = 0;
foreach my $csv2 (keys %csv2hash) {
my $count = 0;
my $value = $csv2hash{$csv2};
foreach my $word (#titlewords) {
my #matches = ( $value=~/\b$word\b/ig );
my $numIncsv2 = scalar(#matches);
#matches = ( $title=~/\b$word\b/ig );
my $numIncsv1 = scalar(#matches);
++$count if $value =~ /\b$word\b/i;
if ($count >= $desired || ($numIncsv1 >= $desired && $numIncsv2 >= $desired)) {
$count = $desired+1;
last;
}
}
if ($count >= $desired) {
print "$csv2\n";
++$matched;
}
}
print "$_\n\n" if $matched;
}
As you can see i've created a find_year subroutine which can be used to discover if the title contains a year in the 20th or 21st century (19xx or 20xx). I asked a question a few days ago which would allow me to assign a result to a set of conditions which involve matching a year and Borodin provided a great answer here.
Perl- What function am I looking for? Assigning multiple rules to a specified outcome
I want the same conditions to apply to now, only this time the script will be comparing dates in the title of the csv's rather than standard input and a data list (as in the previous question).
What I now want to do is include this logic as a function in my word matching script so that if the condition met in my previous question are considered Pass, then perform the word matching part of the script (i.e. 5 words match). If they match the Fail condition, then skip comparing the lines and move onto the next one (i.e. don't bother with the 5 matching word element of the script). The Pass and Fail result's don't have to be printed out, I am just using these words to describe the rules of the year comparison condition in my previous question.
example for csv1:
14564564,1987 the door to the other doors,546456,47878787
456456445,Mullholland Drive is the bets film ever 1959,45454545,45454545
456456445,The Twin Peaks forget that stuff,45454545,45454545
454654564, 1939 hello good world you are great ,45456456, 54564654
example for csv2:
154465454,the other door was the door to 1949,546456,478787870
2156485754,Mullholland Drive is the bets film ever 1939,45454545,45454545
87894454,Twin Peaks forget that stuff 1984,45454545,45454545
2145678787, 1939 good lord you are great ,787425454,878777874
Current result before year_match subroutine is incorporated:
2156485754,Mullholland Drive is the best film ever 1939,45454545,45454545
456456445,Mullholland Drive is the best film ever 1959,45454545,45454545
87894454,Twin Peaks forget that stuff 1984,45454545,45454545
456456445,The Twin Peaks forget that stuff,45454545,45454545
2145678787, 1939 good lord you are great ,787425454,878777874
454654564, 1939 hello good world you are great ,45456456, 54564654
Desired result after match_year subroutine is incorporated:
87894454,Twin Peaks forget that stuff 1984,45454545,45454545
456456445,The Twin Peaks forget that stuff,45454545,45454545
2145678787, 1939 good lord you are great ,787425454,878777874
454654564, 1939 hello good world you are great ,45456456, 54564654
I can get my head around Borodin's answer to my previous question, but as this script I'm working on is difficult to read (IMO noob opinion anyway!), I'm having trouble working out how I can incorporate this new function into it.
I review algorithm. Replaced many csv2 loops to hash of words containing list of csv2 rows numbers. Preliminary check's of years no longer required.
#!/usr/bin/perl
#use Data::Dumper;
#####CREATE CSV2 DATA
open CSV2, "<csv2" or die;
my #csv2=<CSV2>;
close CSV2;
my %words2; # $words2{lower_case_word}->{csv2_row_number}->word_count
my $i=0; # csv2 row number
my %c2year; # Years of csv2 row numbers
for(#csv2) {
chomp;
for((split /,\s*/)[1]=~/(\w+)/g) { # list words in title
$words2{lc($_)}{$i}++;
$c2year{$i}=$_ if(/^(19|20)\d\d$/);
}
$i++;
}
#print Dumper(\%words2);
###### READ CSV1 DATA
my $desired = 5; # Desired matching number of words
open CSV1, "<csv1" or die;
while (<CSV1>) {
chomp; #removes new lines
my %rows=(); # $rows{csv2_row_number} => number_of_matched_words_in_row
my $matched = 0;
my ($title) = (split /,\s*/)[1]; #/ creates variable of title
my %words;
my $year=0;
####### get words and filter it
$words{lc($_)}++ for
grep {
$year=$_ if(/^(19|20)\d\d$/); # Years present in word list
!/^(rare|vol|volume|issue|double|magazine|mag)$/i
} $title=~/(\w+)/g; #/
###### The comparison algorithm
for(keys(%words)) {
# my $word=$_; # <-- if need count words
if($words2{$_}) {
for(keys(%{$words2{$_}})) {
$rows{$_}++; # <-- OR $rows{$_}+=$words{$word} OR/AND +=$words2{$word}{$_}
}
}
}
# print Dumper(\%rows);
for(keys(%rows)) {
if ( ($rows{$_} >= $desired)
&& (!$year || !$c2year{$_} || $year==$c2year{$_} )
) {
print "$year<=>$c2year{$_} csv2: ",$csv2[$_],"\n";
++$matched;
}
}
print "csv1: $_\n\n" if $matched;
}
Uncomment use Data::Dumper and print Dumper(...) for hash's review.
If need consider count of same words, then:
###### The comparison algorithm
for(keys(%words)) {
my $W=$_;
if($words2{$_}) {
for(keys(%{$words2{$_}})) {
$rows{$_} += $words{$W} < $words2{$W}{$_} ? $words{$W} : $words2{$W}{$_};
# $words{$W} - same word count in csv1, $words2{$W}{$_} - count in csv2
}
}
}
# print Dumper(\%rows);

Perl: Want to add and then average columns of tab delimited data

Data is a table that includes names in the first row and first column so I keep getting a non-numeric value error. I figured out how to ignore the first row by using if ($row[0] ne "retrovirus" ), but I don't know how to ignore the first column. I am new to programming and having a really hard time understanding arrays and how to get them to work. How do I split my data into columns of numbers excluding the words and add them together?
This is what I have so far, and its giving incorrect answers.
#!/usr/bin/perl -w
use strict;
# Part A. Computing the average bp length of the virus's
# genomes and each individual gene in the text file.
my $infile = "lab1_table.txt";
open INFILE, $infile or die "$infile: $!";
my #totals = ();
while (my $line = <INFILE>){
chomp $line;
my $total = 0;
my $n = 0;
# Splitting into columns
my #row = split /\t/, $line;
# Working through and adding up each column
foreach my $element (#row) {
# Ignoring first line with headings
if ($row[0] ne "retrovirus" ){
$total = $total + $element;
print "$total \n";
}
}
}
close INFILE;
If you totally don't care about the first element of the row, just use shift(#row)
before the foreach loop. Or if you want to preserve the original values you can get the elements from the second to the last:
#!/usr/bin/perl -w
use strict;
# Part A. Computing the average bp length of the virus's
# genomes and each individual gene in the text file.
my $infile = "lab1_table.txt";
open INFILE, $infile or die "$infile: $!";
while (my $line = <INFILE>)
{
chomp $line;
my $total = 0;
# Splitting into columns
my #row = split /\t/, $line;
# Working through and adding up each column
if ($row[0] ne "retrovirus" )
{
map { $total += $_ } #row[1..(scalar(#row) - 1)];
print "$total \n";
}
}
close INFILE;

Empty array in a perl while loop, should have input

Was working on this script when I came across a weird anomaly. When I go to print #extract after declaring it, it prints correctly the following:
------MMMMMMMMMMMMMMMMMMMMMMMMMM-M-MMMMMMMM
------SSSSSSSSSSSSSSSSSSSSSSSSSS-S-SSSSSDTA
------TIIIIIIIIIIIIITIIIVVIIIIII-I-IIIIITTT
Now the weird part, when I then try to print or return #extract (or $column) inside of the while loop, it comes up empty, thus rendering the rest of the script useless. I've never come across this before up until now, haven't been able to find any documentation or people with similar problems as mine. Below is the code, I marked with #<------ where the problems are and are not, to see if anyone can have any idea what is going on? Thank you kindly.
P.S. I am utilizing perl version 5.12.2
use strict;
use warnings;
#use diagnostics;
#use feature qw(say);
open (S, "Val nuc align.txt") || die "cannot open FASTA file to read: $!";
open (OUTPUT, ">output.txt");
my #extract;
my $sum = 0;
my #lines = <S>;
my #seq = ();
my $start = 0; #amino acid column start
my $end = 10; #amino acid column end
#Removing of the sequence tag until amino acid sequence composition (from >gi to )).
foreach my $line (#lines) {
$line =~ s/\n//g;
if ($line =~ />/g) {
$line =~ s/>.*\]/>/g;
push #seq, $line;
}
else {
push #seq, $line;
}
}
my $seq = join ('', #seq);
my #seq_prot = join "\n", split '>', $seq;
#seq_prot = grep {/[A-Z]/} #seq_prot;
#number of sequences
print OUTPUT "Number of sequences:", scalar (grep {defined} #seq_prot), "\n";
#selection of amino acid sequence. From $start to $end.
my #vertical_array;
while ( my $line = <#seq_prot> ) {
chomp $line;
my #split_line = split //, $line;
for my $index ( $start..$end ) { #AA position, extracts whole columns
$vertical_array[$index] .= $split_line[$index];
}
}
# Print out your vertical lines
for my $line ( #vertical_array ) {
my $extract = say OUTPUT for unpack "(a200)*", $line; #split at end of each column
#extract = grep {defined} $extract;
}
print OUTPUT #extract; #<--------------- This prints correctly the input
#Count selected amino acids excluding '-'.
my %counter;
while (my $column = #extract) {
print #extract; #<------------------------ Empty print, no input found
}
Update: Found the main problem to be with the unpack command, I thought I could utilize it to split my columns of my input at X elements (43 in this case). While this works, the minute I change $start to another number that is not 0 (say 200), the code brings up errors. Probably has something to do with the number of column elements does not match the lines. Will keep updated.
Write your last while loop the same way as your previous for loop. The assignment
my $column = #extract
is in scalar context, which does not give you the same result as:
for my $column (#extract)
Instead, it will give you the number of elements in the array. Try this second option and it should work.
However, I still have a concern, because in fact, if #extract had anything in it, you would obtain an infinite loop. Is there any code that you did not include between your two commented lines?

Perl. While loop behavior changes with seemingly inncocuous addition of command

The following code reads a file that contains many lines. Some lines of the file contain four elements. Other lines contain only a first element, followed single spaces separated by tabs (it is a tab delimited file). That is, some lines are "full" and others are "blank".
The point of this script is to read the data file, find an instance of a blank line, then remember the immediately preceding line (a full line), scroll to find all consecutive blank lines until the next full line is reached. This set of lines, consecutive blank lines flanked by immediately preceding full line and immediately succeeding full line, is to be used by a subroutine that will apply linear interpolation to "fill in" the blank lines. The information in the flanking full lines for each set will be used in the interpolation step. The script was an answer to a previously posted question, and was provided kindly by user #Kenosis. It is duplicated here but with some very minor changes in its layout---not as neat as #Kenosis originally proposed. You can see this interaction at Perl. Using until function
#!/usr/bin/perl
use strict; use warnings;
die "usage: [ map positions file post SAS ]\n\n" unless #ARGV == 1;
my $mapfile = $ARGV[ 0 ];
open( my $FILE, "<$mapfile" );
my #file = <$FILE>;
for ( my $i = 1 ; $i < $#file ; $i++ ) # $#file returns the index of the last element in #file
{
if ( $file[$i] =~ /(?:\t\s){3}/ ) # if a blank line is found
{
print $file[ $i - 1 ]; # print preceding line
while ( $file[$i] =~ /(?:\t\s){3}/ and $i < $#file ) # keep printing so long as they are blank
# or end of file
{
#print $file[ $i++ ] # one-column, blank line
}
print $file[ $i ]; # print the succeeding full line
} # if
} # for
The problem comes when I try to insert a modification.
my #collect = (); # array collects a current set of consecutive lines needed for linear interpolation
my #file = <$FILE>;
for ( my $i = 1 ; $i < $#file ; $i++ ) # $#file returns the index of the last element in #file
{
if ( $file[$i] =~ /(?:\t\s){3}/ ) # if a blank line is found
{
print $file[ $i - 1 ]; # print preceding line
push( #collect, $file[ $i - 1 ] );
while ( $file[$i] =~ /(?:\t\s){3}/ and $i < $#file ) # keep printing so long as they are blank
# or end of file
{
#print $file[ $i++ ]; # one-column, blank line
push( #collect, $file[ $i++ ] )
}
print $file[ $i ]; # else, succeeding full line
push( #collect, $file[ $i ] );
} # if
} # for
The culprit is in the while loop. Adding the push command there changes the behavior of the script. The script is no longer printing all the lines as the first script above. Why does adding that command change how the script is supposed to work?
What are you trying to do in that push line?
It includes the expression $i++, which adds 1 to the value of $1, so each iteration of that while loop will be jumping down another line in the file.
Do you just mean $i + 1?
Are you really adding a second line of code that increments $i? $i += 1 is not the same as $i += 2

Perl: Assigning References to Array Elements in a Foreach Loop

I really wanted to figure this out myself, but my face is now hurting from continually running into this brick wall.
I'm trying to load 9 text files, each consisting of a matrix of 7 rows of 7 characters seperated by spaces and then save each referenced matrix to an element in an array. I am reading in each file just fine, but when I go to access my array all of the elements are the same. I've been searching for a solution and either my question isn't answered anywhere, or (more likely) I'm not understanding the answer. Here's the problem section of my code:
my #boardarray = (1, 2, 3, 4, 5, 6, 7, 8, 9);
sub LoadBoards {
my (#board, $infile, #allboards);
my $i = 1;
#allboards = #boardarray;
foreach (#allboards) {
my $infile = "board" . $i . "\.brd";
open FILE, "< $infile" or die $!;
my $line = 0;
while (<FILE>) {
chomp $_;
my #chars = split (/ /,$_);
$board[$line] = [#chars];
$line++;
}
my $tempboard = \#board;
DisplayOneBoard($tempboard); print ("\n"); #Test A
$boardarray[$i-1] = \#board; #Problem line?
DisplayOneBoard($boardarray[$i-1]); print ("\n"); #Test B
DisplayOneBoard($boardarray[0]); print ("\n----\n"); #Test C
$i++;
}
}
-I've tried assinging variables as the elements of #boardarray with no change.
-I was using #boardarray in the foreach loop and changed it to the copied #allboards with no improvement.
I expect the 'Test A' and 'Test B' lines to be the same and for the 'Test C' line to stay the first matrix I loaded in. However, all three are the same for every iteration.
(For iteration 1 they are all matrix 1. For iteration 2 they are all matrix 2, etc.)
At the end all the elements are the exact same matrix (matrix 9).
Any assistance would be appreciated. Thanks.
The problem is that you are re-using the same #board each time through your loop. When you push a reference to that board onto #boardarray, you are pushing a reference pointing the same #board each time. The fix is simple, just move my #board to the inside of your foreach loop; this creates a new #board each time through.
You might have better luck by breaking down the code a bit more and using arrays as stacks with push/pop:
sub load_file {
my ($filename) = #_;
open my $file, '<', $filename or die $!;
my #array;
while (<$file>) {
chomp $_;
my #chars = split (/ /,$_);
push #array, \#chars; ### adds a reference to the char line
### array to the end of the array
}
return \#array; ### return a ref to the 2-d array
}
sub load_files {
my ($num) = #_;
my %boards; ### A hash, so we can refer to loaded arrays
### with a string ID
for my $filenum ( 1 .. $num ) {
my $filename = "board" . $filenum . "\.brd";
$boards{$filenum} = load_file($filename);
}
return \%boards; ### return a ref to the hash of 2-d arrayrefs
}
### use it now...
my $boards = load_files(9); ### load 9 files.
DisplayOneBoard($boards->{6}); ### dereference our hashref, pass board in
### key '6'to be displayed

Resources