The program that I am writing needs to do this:
- read every line of a file
- if the line contains an ordered pair (x,y) store the ordered pair
- before the next ordered pair, there will be a line of the file that starts with “Results”
- store the ordered pair at the end of that line as a “value” and “error”
- print out the corresponding x, y, value, error in CSV format
- read the next (x,y) value and so on, the (x,y) lines and (value,error) lines will alternate in the file
This is not a homework assignment. As you can see, I already have code that works down to 17 lines. I’m wondering if I can accomplish this task with any fewer lines or cleaner code, while maintaining at least the level of readability that this version has, and maintaining Perl style (such as the line break between includes and the first executable line).
The line that I am least thrilled with is
if (defined($x) && defined($y) && defined($val) && defined($err))
Is there a better way to do an an assertion to take care of the alternating data in the file? If I don’t use the defined() function, the program does not function as intended, because some of the x and y coordinates are 0 values.
#!/usr/bin/perl
use strict;
print "X,Y,Val\n";
foreach (@ARGV){
open log,$_ or die $!;
my ($x,$y,$val,$err);
while(<log>){
chomp;
($x,$y) = ($1,$2) if (/\((\d*|-\d*),(\d*|-\d*)\)/);
($val,$err) = ($1,$2) if (/^Results.*\((.*),(.*)\)$/);
if (defined($x) && defined($y) && defined($val) && defined($err)){
print "$x,$y,$val:$err\n";
($x,$y,$val,$err) = undef;
}
}
}
Thank you everyone for the answers, I’m learning a lot of new Perl syntax.
I’ve figured out how to get this script down to 10 lines. I was challenging myself on the number of lines in which I could write this.
#!/usr/bin/perl
use strict;
print "X,Y,Val\n";
open LOG,"<@ARGV[0]" or die $!;
while(<LOG>){
chomp;
print "$1,$2," if (/\((\d*|-\d*),(\d*|-\d*)\)/);
print "$1:$2\n" if (/^Results.*\((.*),(.*)\)$/);
}
Another Update. Using the information in the answers, I was able to get this down to 8 lines. I also improved the regex and made sure that the header would only be printed once if multiple files were provided.
#!/usr/bin/perl
use strict;
while(<>){
print "X,Y,Val\n" if ($. == 1);
print "$1,$2," if (/.*\((-?\d+),(-?\d+)\)/);
print "$1:$2\n" if (/^Results.*\((.*)\).*\((.*)\)$/);
}
I would switch to reading two lines, rather than one:
One of the benefits of this approach is that your variables are now properly scoped. A simpler version of this program is:
Another variant that takes into account the possibility of lines between the two lines we care about. It assumes the first coordinate pair is the right one:
And this one handles the case where you want the last coordinate line: