CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Beginner:
Problems with homework assignment.

 



swizzlestix917
New User

Mar 28, 2014, 4:09 PM

Post #1 of 4 (2862 views)
Problems with homework assignment. Can't Post

So this is a homework assignment that I have to do:


Consider a file that consists of lines like the following:

r1y2g3g4y2r4r3y5y3g3g6g2r1

We would like to merge all amounts with the same tag together, while preserving the order. For instance, the following line should become:

r 1 y 2 g 7 y 2 r 7 y 8 g 11 r 1
Write a program that reads an input file with letters and numbers
The program must accept the input and output file names as parameters. The input file can have multiple lines, and each line should be processed separately.

This is the code I've written:


Code
#!/usr/bin/perl 

$filename = $ARGV[0];
open (file, "@ARGV") || die "File cannot open:$!";

@everything = <file>;

@var = split ( / /, @everything );

my $last;
my $temp = 0;
my $same = false;

@list1 = ();

foreach (@var){
if ($_ =~ /[a...z]*/) {
if ( $_ == $last ) {
$same = $true;
}
else{
$last = $_;
push @list1, $temp;
push @list1, $_;
$same = $false;
}
}else{
if ($same) {
$temp = $temp + $_;
}else{
$temp = $_;
push @list1, $temp;
}
}
}
print "@list1 \n";

close (file);


My professor told me that perl does not accept 'true' and 'false' as true and false so I'm wondering what I should put in there..

I need true to be if the letter match. I need false to be when they don't. How do I write that in code?


(This post was edited by FishMonger on Mar 28, 2014, 4:14 PM)


BillKSmith
Veteran

Mar 29, 2014, 7:18 AM

Post #2 of 4 (2842 views)
Re: [swizzlestix917] Problems with homework assignment. [In reply to] Can't Post

Always use the pragmas

Code
use strict; 
use warnings;


Fix all the errors that they report.


In perl, the values undef, null string, and numeric zero are all 'false'. All other values are 'true'. By convention, we usually use 1 for 'true' and 0 for 'false'. Note: even the string 'false' is a true value.

Your idea of splitting the input string into an array of characters will work as long as you are sure that the tags are all only one character long and that all values consist of a single digit. (both true in your example) Note: You intend to split on a null string, not on a space. Use // rather than / / in your split.


There are at least two errors in your regular expression.
The '..' in your character class does do what you expect. Use '-' instead. Your use of '*' causes your pattern to match any string. Use '+' instead to require at least one match. (In this case, there is only one character in the string. Omit the repetition)

You have serious problems related to multiple lines. I suggest that you write and debug your code for a single line file. Then generalize it. Ask for help if you need it.
Good Luck,
Bill


Laurent_R
Veteran / Moderator

Mar 29, 2014, 11:49 AM

Post #3 of 4 (2836 views)
Re: [swizzlestix917] Problems with homework assignment. [In reply to] Can't Post

A few more comments in addition to the very good advicve provided by bill.


Code
$filename = $ARGV[0];  
open (file, "@ARGV") || die "File cannot open:$!";


Why are you assigning the argument to $filename and don't use $filename afterwards? Your syntax for opening a file is what was used 20 years ago, there are better ways today. It is better to use lexical file handles, to specify the open mode (write, read, append, etc.) and to specify the name of the file that cannot be opened (in this case, you are opening only one file, but when you will open hald a dozen files, it will be good to know which one could not be opened). I would suggest replacing the two lines above with this:


Code
use strict; 
use warnings;
my $filename = $ARGV[0]; # or : my $filename = shift;
open my $file_handle, "<", $filename or die "Cannot open file $filename : $!";


Next line, you have:

Code
@everything = <file>;


This "slurps" the whole file into an array in memory. Don't do that unless you have to: when you will read files with several GB of data, your program will fail with an "out of memory" error message. You should rather read your file line by line and process each line before going to the next. Something like this:

Code
while (my $line = <$file_handle>) { 
# now process the current line
}

The next line is wrong:

Code
@var = split ( / /, @everything );

This isn't going to work: you are splitting on spaces, but your input has no spaces. In addition, splitting an array of strings does not work, it will split only the first string. If you want to get the individual characters, you need to split on an empty pattern, for example (using the variables I used previously):

Code
my @characters = split  //, $line ;


Since this is homework, I can't go through all your code, I hope this gave you some clues on better practices.

One additional note: your assignment says that the output filename should be given as an argument to your program, you seem to have forgotten that part.

Hope this helps.


Chris Charley
User

Mar 29, 2014, 1:59 PM

Post #4 of 4 (2830 views)
Re: [swizzlestix917] Problems with homework assignment. [In reply to] Can't Post

To show what BillKSmith and Laurent_R suggested, I've outlined what will work but not the completed program. Notice it use strict and warnings. That makes it necessary to declare your variables with 'my', always a good practice. It is almost always done in Perl programs.

I've fixed the errors BillKSmith caught and Laurent_R mentioned.

Code
#!/usr/bin/perl 
use strict;
use warnings;

my $read = $ARGV[0];
my $write = $ARGV[1];

open (my $in, "<", $read) || die "Cannot open read file '$read': $!";
open (my $out, ">", $write) || die "Cannot open write file '$write': $!";

while (my $line = <$in>) {
my @list;
my $last; # $last begins undefined
my $sum;
my $same = 0; # same starts out false

#my @var = split //, $line; # only works if there is 1 letter followed by 1 digit
my @var = $line =~ /(\D+)(\d+)/g;

foreach (@var) {
if (defined $last) {
if (/([a-z]+)/) { # if letter(s)
if ($1 eq $last) {
$same = 1; # $same is now 'true'
}
else {
# code goes here (I had 3 lines)
}
}
else { # if digit(s)
if ($same == 1) {
# code goes here (1 line)
}
else {
# code goes here (1 line)
}
}
}
else {
$last = $_;
}
}
push @list, $last, $sum;
#print $out "@list\n";
}
close $in or die $!;
close $out or die $!;



(This post was edited by Chris Charley on Mar 29, 2014, 2:45 PM)

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives