views:

177

answers:

3

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 heck 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...

+6  A: 

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.

Ether
+7  A: 

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.

Chris Lutz
Note that unlike @Ether's similar (but more explicit) answer, this will tolerate irregular input and jagged arrays. See his answer if you don't want that (although his answer may cause problems if someone tries to enter a jagged array).
Chris Lutz
+1 for the explanation
meder
More Perl-y! Me want `@a=map{[split]}<>` !
mobrule
Sorry, got code golf on the brain. But the `chomp` is superfluous with a bare `split`.
mobrule
@mobrule - If you can show me that `my @a = map { [ split ] } <>` gets translated down to the same bytecode as what I posted, rather than reading in the entire `<>` filehandle _and then_ `map()` ing it, I'll add that to my answer.
Chris Lutz
I forgot about the subtleties of bare `split` - Thanks.
Chris Lutz
@Chris Lutz - Thank You so much! Thank you for explaining it to a n00b like me.
Mawnster
+4  A: 

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.

hobbs
+1 I like that. If only there were a nicer way to pass around old-style filehandles.
Chris Lutz
how do I go about handling multiple matrices with the above code? Say I have 1 2 34 5 64 5 61 2 21 2 28 9 97 8 15 6 39 4 8how would I multiply them all together? wouldn't I have to count how many matrices where input then create a new matrix for each? I'm lost.
Mawnster