How do I build a 2d matrix using STDIN in Perl?
How do I build a 2d matrix using STDIN?
If I input a matrix like so:
1 2 3
4 5 6
7 5 6
7 8 9
4 5 6
3 3 3
how do I input this and create two matrices out of this?
Here's my code so far
while (defined ($a=<STDIN>)) {
chomp ($a);
push @a,($a);
}
This is just for the input.
My understanding is I can just add each row to a stack. When the matrices are all put in I can take each line, break by space to create an array. I then need to create an array reference and push this reference into an array to create my matrix. How the h开发者_StackOverfloweck do I do this? Is there an easier way? I've been bashing my head on this for 3 days now. I feel pretty damn stupid right now...
Let's make that code you have a little more Perl-y, and we'll do everything you need done in one pass:
my @a = ();
while(<>) {
push @a, [ split ];
}
This is taking a lot out of your answer, so I'll opt to explain it, rather than aiming for John Wayne-like answering reflexes. We'll start with your line here:
while(defined(my $a = <STDIN>))
Perl users know that many loops will implicitly use the $_
variable. If you need lots of nested loops, you should avoid using that variable, and use well-named variables for each level of looping, but in this case we only have one level, so let's go ahead and use it:
while(defined($_ = <STDIN>))
Now, Perl is kind enough to understand that we want to test for defined()
ness a lot, so it will allow us to shorten that to this:
while(<STDIN>)
This is implicitly translated by Perl as assigning the line read to $_
and returning true as long as the result is defined (and therefore until end-of-file occurs). However, Perl gives us one more trick:
while(<>)
This will loop over STDIN
or, if arguments are given on the command line, it will open those as files and loop over them. So this still reads from STDIN
:
./myscript.pl
But we can also read from one or more files:
./myscript.pl myfile [myfile2 [myfile3 ...]]
It's easier and more intuitive than using the shell to do the same (though this will still work):
cat myfile [myfile2 [myfile3 ...]] | ./myscript.pl
If you don't want this behavior, you can change it back to <STDIN>
, but consider keeping it.
The loop is:
push @a, [ split ];
First, split()
with no arguments is identical to split /\s+/, $_
(i.e. it splits the $_
string on occurrences of whitespace characters), and due to the subtleties of split
empty trailing fields are removed, so a chomp()
is unnecessary. Then, []
creates an anonymous array reference (which, in this case, contains the contents of our split $_
string). Then, we push that array reference onto @a
. Simple as pie, you now have a two-dimensional matrix from your standard input.
Try this:
use strict;
use warnings;
use Data::Dumper;
my @matrix;
while (my $line = <>) {
chomp $line;
my @row = split /\s+/, $line, 3;
push @matrix, \@row;
}
print Dumper(\@matrix);
Instead of using <STDIN>
explicitly, you can read from either stdin or a piped file with <>
.
Inputting one matrix gives the result:
$VAR1 = [
[
'1',
'2',
'3'
],
[
'4',
'5',
'6'
],
[
'7',
'8',
'9'
]
];
From here you should be able to see what you need to do to read in two matrices.
The other answers seem to be missing the requirement to read multiple matrices from the same input, breaking on a blank line. There are a few different ways to go about this, including frobbing $/
, but here's one that appeals to me.
# Read a matrix from a handle, with columns delimited by whitespace
# and rows delimited by newlines. A matrix ends at a blank line
# (which is consumed) or EOF.
sub read_matrix_from {
my ($handle) = @_;
my @out;
while (<$handle>) {
last unless /\S/;
push @out, [ split ];
}
return \@out;
}
my @matrices;
push @matrices, read_matrix_from(\*ARGV) until eof();
Season the last part to taste, of course -- you might be using an explicitly opened handle instead of ARGV magic, and you might know in advance how many things you're reading instead of going to EOF, etc.
精彩评论