How do I build a 2d matrix using STDIN in Perl? - 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 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...

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.

Related

Odd "Use of uninitialized value", regular expression error

I'm using Strawberry Perl 5 on Windows 10. It does seem like my regular expressions are broken or regex101 won't tell me the truth. I want to catch 'num km'. Even tho my array seems to be the right length it'd often say"Use of uninitialized value".
my $string = "^ˇ~ --_ 12 km aéeklwa 32 km | \|ġ^ 0 km 23-24 km";
if (#szelmatches = $string =~ /\d+(\-\d+)?\s+km/gm) {
my $number_of_elements = scalar(#szelmatches);
print "Elements in the array : $number_of_elements \n";
}
foreach (#szelmatches) {
print "$_\n";
}
OUTPUT:
Elements in the array : 4
Use of uninitialized value $_ in concatenation (.) or string at C:\misc\perlek\wttr\szel.pl line 16.
I've ran defined() checks but it seems like my array elements are all defined. Changing \- to .{1} occasionaly worked but it is quite annoying to write like this. regex101.com and regexr.com tells me everything is allright.
I know you could write it simpler/shorter/better/faster/nicer etc, but i honestly think this should work. Do you guys have any idea what am i doing wrong?
Firstly, I had to fix a syntax error in your code before I could run it (the closing ) was missing from your if statement). Please cut and paste code, rather than retyping it.
If Perl tells you that it's finding undefs then it's almost certainly right. Using Data::Dumper can show us what is going on.
use warnings;
use Data::Dumper;
my $string = "^ˇ~ --_ 12 km aéeklwa 32 km | \|ġ^ 0 km 23-24 km";
if (#szelmatches = $string =~ /\d+(\-\d+)?\s+km/gm) {
my $number_of_elements = scalar(#szelmatches);
print "Elements in the array : $number_of_elements \n";
}
print Dumper \#szelmatches;
foreach (#szelmatches) {
print "$_\n";
}
That gives us the following:
$VAR1 = [
undef,
undef,
undef,
'-24'
];
So, yes, there are three undefs in your results. Can we work out why?
Well, here's your match operator.
/\d+(\-\d+)?\s+km/gm
It's looking for digits followed by an optional dash and more digits. But it's only that optional part that you're capturing (as it has parentheses around it). And in the first three cases, that optional section doesn't appear. So you get undef for those first three matches.
Let's actually match what you want (the whole digits section, I think) by putting more parentheses around the whole thing.
/(\d+(\-\d+)?)\s+km/gm
Now we get this result:
$VAR1 = [
'12',
undef,
'32',
undef,
'0',
undef,
'23-24',
'-24'
];
That's better. We get all of the matches we want, alongside the original ones. So, that's twice as many matches as we want. That's because we now have two sets of parentheses for each match. We need the first set to match and capture the digit section and the second set to join together the "-" and the "\d+". But we don't need the second set to capture its contents.
If you read the section on "Extended Patterns" in the perlre manual page, you'll see that we can create non-capturing parentheses with (?:...). So let's use that.
/(\d+(?:\-\d+)?)\s+km/gm
And that gives us:
$VAR1 = [
'12',
'32',
'0',
'23-24'
];
Which is, I think, what you wanted.
Update: Re-reading your question, I realise that you wanted the 'km' as well. So I've moved the closing parentheses past that.
/(\d+(?:\-\d+)?\s+km)/gm
And that gives us:
$VAR1 = [
'12 km',
'32 km',
'0 km',
'23-24 km'
];
The warning you see is because $_ is undefined. In Perl, you can have variables that have no value at all. That's undef.
The first thing you want to do in this case is inspect your array. The core Data::Dumper module is good for that. Or you can install Data::Printer from CPAN, which I prefer.
print Dumper \#szelmatches;
foreach (#szelmatches) {
print "$_\n";
}
This will output
$VAR1 = [
undef,
undef,
undef,
'-24'
];
Clearly there are some undefs in the array. This is because you have a capture group (\-\d) that is optional ?. Each time the string gets matched successfully via the /g modifier, it will put all of the capture group results into your array. But the only group you have is optional, so the pattern matches even if there is no -\d going on.
You can visualise this on Debugex. If you want to have a more detailed play-around with it, try the Regexp::Debugger module, which will allow you to step-by-step debug your regex right in your terminal.
You will have to tell us which numbers you actually want to capture.
If all you are after is the second one after the dash (which you do not have to escape, it has no special meaning), then you should not make that capture group optional.
Two problems.
When a capture is conditional (e.g. (...)?) and it doesn't match anything, it captures undef.
When there's one or more captures, the match returns the capture text rather than the entire text matched.
The solution is to remove the useless and problem-causing capture. Replace
if ( my #szelmatches = $string =~ /\d+(\-\d+)?\s+km/g )
with
if ( my #szelmatches = $string =~ /\d+(?:\-\d+)?\s+km/g )

Perl use variable as a file name

How to create a file with name containing variables with underscore between them. I need to create a file with name like this $variable1_$vraiable2_$variable3.txt
#values=split(/\./, $line)
my $fpga_name=$values[0];
my $block_name=$values[1];
my $mem_name=$values[2];
my $memfilename="mem_init/$fpga_name_$block_name_$mem_name.txt";
open(WRITE_MEM_FILE, ">>memfilename");
print WRITE_MEM_FILE "$line \n";
You can simply wrap all of the variables in curly braces:
my $memfilename="mem_init/${fpga_name}_${block_name}_${mem_name}.txt";
Keep in mind you need a $ before memfilename in your open statement, otherwise you will just get the literal string:
open(WRITE_MEM_FILE, ">>$memfilename");
The question is whether you need the intermediate array, and the three extra variables. If not, you can write the whole thing as:
my $memfilename = sprintf(
'%s_%s_%s.txt',
split(/[.]/, $line, 3), # whether you want 3 here depends on your input
);
If you do need the three intermediate variables, you can still skip the creation of the #value array and write something more legible than interpolating three variables into a string:
my ($fpga_name, $block_name, $mem_name) = split /[.]/, $line, 3;
my $memfilename = sprintf '%s_%s_%s.txt', $fpga_name, $block_name, $mem_name;
Using sprintf yields code that is much more readable than interpolating three variables, the braces, the underscores, the sigils etc.
Alternatively, you could also use:
my $memfilename = sprintf '%s.txt', join('_', split /[.]/, $line, 3);
Again, whether you want the third argument to split depends on your input.
Finally, if you find yourself doing this in more than one place, it would help to put it in a function
sub line_to_memfilename {
my $line = shift;
# ...
return $memfilename;
}
so if the format ever changes, you only need to make the change in one place.
Indicate where the variable names begin & end by writing ${varname}:
my $memfilename="mem_init/${fpga_name}_${block_name}_${mem_name}.txt";

Skipping particular positions in a string using substitution operator in perl

Yesterday, I got stuck in a perl script. Let me simplify it, suppose there is a string (say ABCDEABCDEABCDEPABCDEABCDEPABCDEABCD), first I've to break it at every position where "E" comes, and secondly, break it specifically where the user wants to be at. But, the condition is, program should not cut at those sites where E is followed by P. For example there are 6 Es in this sequence, so one should get 7 fragments, but as 2 Es are followed by P one will get 5 only fragments in the output.
I need help regarding the second case. Suppose user doesn't wants to cut this sequence at, say 5th and 10th positions of E in the sequence, then what should be the corresponding script to let program skip these two sites only? My script for first case is:
my $otext = 'ABCDEABCDEABCDEPABCDEABCDEPABCDEABCD';
$otext=~ s/([E])/$1=/g; #Main cut rule.
$otext=~ s/=P/P/g;
#output = split( /\=/, $otext);
print "#output";
Please do help!
To split on "E" except where it's followed by "P", you should use Negative look-ahead assertions.
From perldoc perlre "Look-Around Assertions" section:
(?!pattern)
A zero-width negative look-ahead assertion.
For example /foo(?!bar)/ matches any occurrence of "foo" that isn't followed by "bar".
my $otext = 'ABCDEABCDEABCDEPABCDEABCDEPABCDEABCD';
# E E EP E EP E
my #output=split(/E(?!P)/, $otext);
use Data::Dumper; print Data::Dumper->Dump([\#output]);"
$VAR1 = [
'ABCD',
'ABCD',
'ABCDEPABCD',
'ABCDEPABCD',
'ABCD'
];
Now, in order to NOT cut at occurences #2 and #4, you can do 2 things:
Concoct a really fancy regex that automatically fails to match on given occurence. I will leave that to someone else to attempt in an answer for completeness sake.
Simply stitch together the correct fragments.
I'm too brain-dead to come up with a good idiomatic way of doing it, but the simple and dirty way is either:
my %no_cuts = map { ($_=>1) } (2,4); # Do not cut in positions 2,4
my #output_final;
for(my $i=0; $i < #output; $i++) {
if ($no_cuts{$i}) {
$output_final[-1] .= $output[$i];
} else {
push #output_final, $output[$i];
}
}
print Data::Dumper->Dump([\#output_final];
$VAR1 = [
'ABCD',
'ABCDABCDEPABCD',
'ABCDEPABCDABCD'
];
Or, simpler:
my %no_cuts = map { ($_=>1) } (2,4); # Do not cut in positions 2,4
for(my $i=0; $i < #output; $i++) {
$output[$i-1] .= $output[$i];
$output[$i]=undef; # Make the slot empty
}
my #output_final = grep {$_} #output; # Skip empty slots
print Data::Dumper->Dump([\#output_final];
$VAR1 = [
'ABCD',
'ABCDABCDEPABCD',
'ABCDEPABCDABCD'
];
Here's a dirty trick that exploits two facts:
normal text strings never contain null bytes (if you don't know what a null byte is, you should as a programmer: http://en.wikipedia.org/wiki/Null_character, and nb. it is not the same thing as the number 0 or the character 0).
perl strings can contain null bytes if you put them there, but be careful, as this may screw up some perl internal functions.
The "be careful" is just a point to be aware of. Anyway, the idea is to substitute a null byte at the point where you don't want breaks:
my $s = "ABCDEABCDEABCDEPABCDEABCDEPABCDEABCD";
my #nobreak = (4,9);
foreach (#nobreak) {
substr($s, $_, 1) = "\0";
}
"\0" is an escape sequence representing a null byte like "\t" is a tab. Again: it is not the character 0. I used 4 and 9 because there were E's in those positions. If you print the string now it looks like:
ABCDABCDABCDEPABCDEABCDEPABCDEABCD
Because null bytes don't display, but they are there, and we are going to swap them back out later. First the split:
my #a = split(/E(?!P)/, $s);
Then swap the zero bytes back:
$_ =~ s/\0/E/g foreach (#a);
If you print #a now, you get:
ABCDEABCDEABCDEPABCD
ABCDEPABCD
ABCD
Which is exactly what you want. Note that split removes the delimiter (in this case, the E); if you intended to keep those you can tack them back on again afterward. If the delimiter is from a more dynamic regex it is slightly more complicated, see here:
http://perlmeme.org/howtos/perlfunc/split_function.html
"Example 9. Keeping the delimiter"
If there is some possibility that the #nobreak positions are not E's, then you must also keep track of those when you swap them out to make sure you replace with the correct character again.

Simple multi-dimensional array with loop in perl

I'm trying to use an array and a loop to print out the following (basically for each letter of the alphabet, print each letter of the alphabet after it and then move on to the next letter). I'm new to perl, anyone have any quick words of :
aa
ab
ac
ad
...
ba
bb
bc
bd
...
ca
cb
...
Currently I have this, but it only prints a single character alphabet...
#arr = ("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z");
$i = #arr;
while ($i)
{
print $arr[$i];
$i--;
}
Using the range operator and the ranges you want to target:
use strict;
use warnings;
my #elements = ("aa" .. "zz");
for my $combo (#elements)
{
print "$combo\n";
}
You can utilize the initial 2 letters till the ending 2 letters you want as ending and the for will take care of everything.
This really isn't multi-dimensional array work, if it were you'd be working with stuff like:
my #foo = (
[1,2,3],
[4,7,8,1,2,3],
[2,3],
);
This is really a very basic how do I make a nested loop that iterates over the same array. I'll bet this is homework.
So, I'll let you figure out the nesting bits, but give some help with Perl's loop operators.
!! for/foreach
for (the each is optional) is the real heavy hitter for looping in perl. Use it like so:
for my $var ( #array ) {
#do stuff with $var
}
Each element in #array will be aliased to the $var variable, and the block of code will be executed. The fact that we are aliasing, rather than copying means that if alter the value of $var, #array will be changed as well. The stuff between the parenthesis may be any expression. The expression will be evaluated in list context. So if you put a file handle in the parens, the entire file will be read into memory and processed.
You can also leave off naming the loop variable, and $_ will be used instead. In general, DO NOT DO THIS.
!! C-Style for
Every once in a while you need to keep track of indexes as you loop over an array. This is when a C style for loop comes in handy.
for( my $i=0; $i<#array; $i++ ) {
# do stuff with $array[$i]
}
!! While/Until
While and until operate with boolean loop conditions. That means that the loop will repeat as long as the appropriate boolean value if found for the condition ( TRUE for while, and FALSE for until). In addition to the obvious cases where you are looking for a particular condition, while is great for processing a file one line at a time.
while ( my $line = <$fh> ) {
# Do stuff with $line.
}
!! map
map is an amazingly useful bit of functional programming kung-fu. It is used to turn one list into another. You pass an anonymous code reference that is used to enact the transformation.
# Multiply all elements of #old by two and store them in #new.
my #new = map { $_ * 2 } #old;
So how do you solve your particular problem? There are many ways. Which is best depends on how you want to use the results. If you want to create a new array of the letter pairs, use map. If you are interested primarily in a side effect (say printing a variable) use for. If you need to work with really big lists that come from sort of interator (like lines from a filehandle) use while.
Here's a solution. I wouldn't turn it in to your professor until you understand how it works.
print map { my $letter=$_; map "$letter$_\n", "a".."z" } "a".."z";
Look at perldoc articles, perlsyn for info on the looping constructs, perlfunc for info on map and look at perlop for info on the range operator (..).
Good luck.
Use the range operator (..) for your initialization. The range operator basically grabs a range of values such as numbers or characters.
Then use a nested loop to go through the array one time per character for a total of 26^2 iterations.
Rather than a while loop I've used a foreach loop to go through each item in the array. You could also put 'a' .. 'z' instead of declared #arr as the argument to the foreach loop. The foreach loops below set $char or $char2 to each value in #arr in turn.
my #arr = ('a' .. 'z');
for my $char (#arr) {
for my $char2 (#arr) {
print "$char$char2\n";
}
}
If all you really want to do is print the 676 strings you describe, then:
#!/usr/bin/perl
use warnings;
use strict;
my $str = 'aa';
while (length $str < 3) {
print $str++, "\n";
}
But I smell an "XY problem"...

What does it mean when you try to print an array or hash using Perl and you get, Array(0xd3888)?

What does it mean when you try to print an array or hash and you see the following; Array(0xd3888) or HASH(0xd3978)?
EXAMPLE
CODE
my #data = (
['1_TEST','1_T','1_TESTER'],
['2_TEST','2_T','2_TESTER'],
['3_TEST','3_T','3_TESTER'],
['4_TEST','4_T','4_TESTER'],
['5_TEST','5_T','5_TESTER'],
['6_TEST','6_T','^_TESTER']
);
foreach my $line (#data) {
chomp($line);
#random = split(/\|/,$line);
print "".$random[0]."".$random[1]."".$random[2]."","\n";
}
RESULT
ARRAY(0xc1864)
ARRAY(0xd384c)
ARRAY(0xd3894)
ARRAY(0xd38d0)
ARRAY(0xd390c)
ARRAY(0xd3948)
It's hard to tell whether you meant it or not, but the reason why you're getting array references is because you're not printing what you think you are.
You started out right when iterating over the 'rows' of #data with:
foreach my $line (#data) { ... }
However, the next line is a no-go. It seems that you're confusing text strings with an array structure. Yes, each row contains strings, but Perl treats #data as an array, not a string.
split is used to convert strings to arrays. It doesn't operate on arrays! Same goes for chomp (with an irrelevant exception).
What you'll want to do is replace the contents of the foreach loop with the following:
foreach my $line (#data) {
print $line->[0].", ".$line->[1].", ".$line->[2]."\n";
}
You'll notice the -> notation, which is there for a reason. $line refers to an array. It is not an array itself. The -> arrows deference the array, allowing you access to individual elements of the array referenced by $line.
If you're not comfortable with the idea of deferencing with arrows (and most beginners usually aren't), you can create a temporary array as shown below and use that instead.
foreach my $line (#data) {
my #random = #{ $line };
print $random[0].", ".$random[1].", ".$random[2]."\n";
}
OUTPUT
1_TEST, 1_T, 1_TESTER
2_TEST, 2_T, 2_TESTER
3_TEST, 3_T, 3_TESTER
4_TEST, 4_T, 4_TESTER
5_TEST, 5_T, 5_TESTER
6_TEST, 6_T, ^_TESTER
A one-liner might go something like print "#$_\n" for #data; (which is a bit OTT), but if you want to just print the array to see what it looks like (say, for debugging purposes), I'd recommend using the Data::Dump module, which pretty-prints arrays and hashes for you without you having to worry about it too much.
Just put use Data::Dump 'dump'; at beginning of your script, and then dump #data;. As simple as that!
It means you do not have an array; you have a reference to an array.
Note that an array is specified with round brackets - as a list; when you use the square bracket notation, you are creating a reference to an array.
foreach my $line (#data)
{
my #array = #$line;
print "$array[0] - $array[1] - $array[2]\n";
}
Illustrating the difference:
my #data = (
['1_TEST','1_T','1_TESTER'],
['2_TEST','2_T','2_TESTER'],
['3_TEST','3_T','3_TESTER'],
['4_TEST','4_T','4_TESTER'],
['5_TEST','5_T','5_TESTER'],
['6_TEST','6_T','^_TESTER']
);
# Original print loop
foreach my $line (#data)
{
chomp($line);
#random = split(/\|/,$line);
print "".$random[0]."".$random[1]."".$random[2]."","\n";
}
# Revised print loop
foreach my $line (#data)
{
my #array = #$line;
print "$array[0] - $array[1] - $array[2]\n";
}
Output
ARRAY(0x62c0f8)
ARRAY(0x649db8)
ARRAY(0x649980)
ARRAY(0x649e48)
ARRAY(0x649ec0)
ARRAY(0x649f38)
1_TEST - 1_T - 1_TESTER
2_TEST - 2_T - 2_TESTER
3_TEST - 3_T - 3_TESTER
4_TEST - 4_T - 4_TESTER
5_TEST - 5_T - 5_TESTER
6_TEST - 6_T - ^_TESTER
You're printing a reference to the hash or array, rather than the contents OF that.
In the particular code you're describing I seem to recall that Perl automagically makes the foreach looping index variable (my $line in your code) into an "alias" (a sort of reference I guess) of the value at each stage through the loop.
So $line is a reference to #data[x] ... which is, at each iteration, some array. To get at one of the element of #data[0] you'd need the $ sigil (because the elements of the array at #data[0] are scalars). However $line[0] is a reference to some package/global variable that doesn't exist (use warnings; use strict; will tell you that, BTW).
[Edited after Ether pointed out my ignorance]
#data is a list of anonymous array references; each of which contains a list of scalars. Thus you have to use the sort of explicit de-referencing I describe below:
What you need is something more like:
print ${$line}[0], ${$line}[1], ${$line}[2], "\n";
... notice that the ${xxx}[0] is ensuring that the xxx is derefenced, then indexing is performed on the result of the dereference, which is then extracted as a scalar.
I testing this as well:
print $$line[0], $$line[1], $$line[2], "\n";
... and it seems to work. (However, I think that the first form is more clear, even if it's more verbose).
Personally I chalk this up to yet another gotchya in Perl.
[Further editorializing] I still count this as a "gotchya." Stuff like this, and the fact that most of the responses to this question have been technically correct while utterly failing to show any effort to actually help the original poster, has once again reminded me why I shifted to Python so many years ago. The code I posted works, of course, and probably accomplishes what the OP was attempting. My explanation was wholly wrong. I saw the word "alias" in the `perlsyn` man page and remembered that there were some funky semantics somewhere there; so I totally missed the part that [...] is creating an anonymous reference. Unless you drink from the Perl Kool-Aid in deep drafts then even the simplest code cannot be explained.