Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Newbie question under time constraint

by crazedrat (Initiate)
on Aug 04, 2019 at 16:48 UTC ( [id://11103867]=perlquestion: print w/replies, xml ) Need Help??

crazedrat has asked for the wisdom of the Perl Monks concerning the following question:

This node falls below the community's threshold of quality. You may see it by logging in.

Replies are listed 'Best First'.
Re: Newbie question under time constraint
by roboticus (Chancellor) on Aug 04, 2019 at 19:37 UTC

    crazedrat:

    You'll generally have better luck with your classes if you design your classes further in advance of when they are due.... 8^)

    Anyway, a few notes to help you on your way:

    I tend to use a package like Data::Dump for debugging,as it will print the information in an easy format, so I can verify that the data is or isn't what I expect to see. Instead of your main program, I used this:

    use strict; use warnings; use Employee; use EmployeeList; use Data::Dump 'pp'; my $E1 = new Employee( {name=>'George', hours=>40, hourlywage=>50.00} +); print "What does Data::Dump see for my Employee?\n"; pp($E1); print "\nHow does it print?\n"; $E1->print_student();

    It looks like you were a bit optimistic. The Employee class looks like it constructs itself correctly, but the print_student method does no such thing:

    $ perl classes.pl What does Data::Dump see for my Employee? bless({ hourlywage => 50, hours => 40, name => "George" }, "Employee") How does it print?

    Here, the problem appears to be a simple naming issue--a method named "print_student" implies that (a) it's actually going to print something, and (b) that it's going to print a student. There's nothing in your Employee class that lets us figure out whether it's actually a student or not, so I'm guessing it's a holdover from a previous assignment. I'd suggest renaming your method to indicate what it's actually going to do, which is to format the employee as a string. I'll just settle for calling it "format" for now and updating the code. After making the appropriate edits, I get:

    $ perl classes.pl What does Data::Dump see for my Employee? bless({ hourlywage => 50, hours => 40, name => "George" }, "Employee") How does it print? Employee info: name: George hours: 40 wage: 50

    OK, that went well. Next let's add a couple more Employees, make an EmployeeList, add the Employees to the list and see what we get:

    use strict; use warnings; use Employee; use EmployeeList; use Data::Dump 'pp'; my $E1 = new Employee( {name=>'George', hours=>40, hourlywage=>50.00 +} ); print "What does Data::Dump see for my Employee?\n"; pp($E1); print "\nHow does it print?\n"; print $E1->format(); print "\nLet's create a couple more employees and print them:\n"; my $E2 = new Employee( {name=>'Ellen', hours=>30, hourlywage=>35.00 +} ); my $E3 = new Employee( {name=>'Phideaux', hours=>10, hourlywage=> 5.00 +} ); print $E2->format(), $E3->format(); print "\nNow we'll create an EmployeeList.", "\nWhat does Data::Dump see when it's empty?\n"; my $L = new EmployeeList(); pp($L); print "\nHow does an empty list print?\n"; print $L->format(); $L->set_EmployeeList( [ $E1, $E2, $E3 ] ); print "\nAfter adding George, Ellen and Phideaux, our list looks like: +\n"; pp($L); print "\nHow does it look using the built-in formatter?\n"; print $L->format(); print "\nDoes get_EmployeeList work?\n"; pp($L->get_EmployeeList());

    OK, when we run it, we get:

    $ perl classes.pl What does Data::Dump see for my Employee? bless({ hourlywage => 50, hours => 40, name => "George" }, "Employee") How does it print? Employee info: name: George hours: 40 wage: 50 Let's create a couple more employees and print them: Employee info: name: Ellen hours: 30 wage: 35 Employee info: name: Phideaux hours: 10 wage: 5 Now we'll create an EmployeeList. What does Data::Dump see when it's empty? bless({ EmployeeList => [] }, "EmployeeList") How does an empty list print? Employee info: ARRAY(0x60028cbd8) After adding George, Ellen and Phideaux, our list looks like: bless({ EmployeeList => [ bless({ hourlywage => 50, hours => 40, name => "George" }, "Employ +ee"), bless({ hourlywage => 35, hours => 30, name => "Ellen" }, "Employe +e"), bless({ hourlywage => 5, hours => 10, name => "Phideaux" }, "Emplo +yee"), ], }, "EmployeeList") How does it look using the built-in formatter? Employee info: ARRAY(0x600240278) Does get_EmployeeList work? [ bless({ hourlywage => 50, hours => 40, name => "George" }, "Employee +"), bless({ hourlywage => 35, hours => 30, name => "Ellen" }, "Employee" +), bless({ hourlywage => 5, hours => 10, name => "Phideaux" }, "Employe +e"), ]

    Overall, it's not too bad. We can successfully create the list and add employees to it and fetch it back.

    The big problem at the moment is that the EmployeeList format() function just stringifies the list, making an ugly ARRAY(0x########) string rather than showing us a list of Employees.

    To handle this, in your EmployeeList format() function, you'll want to iterate over each Employee in the list and then format each individual item. To start you along, here's how you can iterate in your format function:

    package EmployeeList; . . . sub format { my $self = shift; for my $anEmployee ( @{ $self->{EmployeeList} } ) { # Do something with the employee, such as: # print "current employee name is: ", $anEmployee->{name}, ".\n" +; } }

    Here are a few suggestions on improving the code:

    • Be careful with your function names: make them do what they say they're going to do or rename them to indicate what they actually do. Writing code is harder when function names mislead you.
    • I'd suggest having methods that can add or delete a single employee from the EmployeeList. Having to pull all the employees out of the list into an array, then adding or deleting an employee to that array and then setting the EmployeeList to that array sounds like a rather painful way to manage the employees.
    • For your query function(s), you're going to have to figure out what sorts of queries you want to do, and how you'd accomplish them. I'd suggest having your query function(s) return a *new* EmployeeList with the selected Employees, so you can refine your queries.
      You could make a single query function that takes a complicated set of arguments to figure out what to do, like:
      # Get a list of employees between ages 30 and 50 who make more than $2 +5/hr my $interesting = $L->query( [ "AGE", "BETWEEN", 30, 50 ], [ "HOURLY", "ABOVE", 25 ], );
      or multiple query functions that take simpler arguments:
      my $interesting = $L->filter_age( 30, 50 ) ->filter_hourly( 25, 99999999999999 );

    My final bit of advice: Rather than writing the entire program at once, build it up feature by feature. If you have two hundred lines of untested code and have an error, you don't really know where that error is. Finding it can be a problem. But if you have a working program that you understand, and then write 20 lines of code to add a new feature, and then have a problem, it's easier to home in on the location of the error. Don't be afraid to add print statements anywhere in your program to see what's happening--you can take them out later when you don't need them. Even better--try to learn the debugger. It can be *very* educational running your program in the debugger since you can stop the code anywhere you want, examine values to see if they hold what you expected or not. You can even change data values to see what would happen in other cases.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: Newbie question under time constraint
by holli (Abbot) on Aug 04, 2019 at 18:49 UTC
    Here you go. My Paypal for the twenty. Please note that a public offer of a reward is legally binding.
    package Employee; use strict; use warnings; sub new{ my ($class, $args) = @_; my $self = bless { name=> $args->{name}, hourlywage => $args->{hourlywage}, hours => $args->{hours} }, $class; } sub pay { my $self = shift; return $self->{hours} * $self->{hourlywage}; } sub gist { my $self = shift; return "Employee info: name: $self->{name} hours: $self->{hours} w +age: $self->{hourlywage}\n" } 1; package EmployeeList; use diagnostics; use strict; use warnings; sub new{ my ($class) = @_; my $self = bless { EmployeeList => [] }, $class; return $self; } sub add_Employee { my $self = shift; my $Employee = shift; push @{ $self->{EmployeeList} }, $Employee; } sub gist{ my $self = shift; my @gist = map { $_->gist } @{ $self->{EmployeeList} }; return join "", @gist; } sub average_hourly_wage{#not even sure where to begin, don't know how +to initialize or access the hash at this point my $self = shift; my $wages; my $count; foreach my $employee ( @{ $self->{EmployeeList} }) { $wages += $employee->{hourlywage}; $count++; } return $wages / $count; } sub find { my $self = shift; my $name = shift; foreach my $Employee ( @{ $self->{EmployeeList} } ) { return $Employee if $Employee->{name} eq $name; } } 1; #use Employee; #use EmployeeList; print "Enter the name of the file to open: "; my $input_file = <STDIN>; chomp $input_file; open(my $file_handle, "<", $input_file) or die "failed to open < input +_file: $!\n"; my $EmployeeList = new EmployeeList(); while (my $row = <$file_handle>){ my @splitrow = split ' ', $row; my $Employee1 = Employee->new({ name =>$splitrow[0], hourlywage =>$splitrow[1], hours =>$splitrow[2] }); $EmployeeList->add_Employee( $Employee1 ); } print $EmployeeList->gist(); print $EmployeeList->average_hourly_wage(), "\n"; print $EmployeeList->find("holli")->pay, "\n";


    holli

    You can lead your users to water, but alas, you cannot drown them.
      Great! Here's the same rewritten into Moose.
      #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; { package Employee; use Moose; use Function::Parameters; use namespace::clean; has name => (is => 'ro', isa => 'Str'); has hourly_wage => (is => 'ro', isa => 'Int'); has hours => (is => 'ro', isa => 'Int'); method pay () { $self->hours * $self->hourly_wage } method gist () { "Employee info: name: " . $self->name . " hours: " . $self->hours . " wage: " . $self->hourly_wage . "\n" } __PACKAGE__->meta->make_immutable } { package EmployeeList; use Moose; use Function::Parameters; use List::Util qw{ sum0 }; use namespace::clean; has employee_list => (is => 'ro', isa => 'ArrayRef[Employee]', traits => ['Array'], handles => { add_employee => 'push', list_employees => 'elements', }, ); method gist () { join "", map $_->gist, $self->list_employees } method average_hourly_wage () { sum0(map $_->hourly_wage, $self->list_employees) / $self->list_employees } method find ($name) { (grep $_->name eq $name, $self->list_employees)[0] } __PACKAGE__->meta->make_immutable } print 'Enter the name of the file to open: '; chomp( my $input_file = <STDIN> ); open my $file_handle, '<', $input_file or die "Failed to open $input_file: $!\n"; my $employee_list= 'EmployeeList'->new; while (my $row = <$file_handle>) { my @splitrow = split ' ', $row; my $employee = 'Employee'->new( name => $splitrow[0], hourly_wage => $splitrow[1], hours => $splitrow[2] ); $employee_list->add_employee($employee); } print $employee_list->gist; say $employee_list->average_hourly_wage; say $employee_list->find('holli')->pay;
      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      My Paypal for the twenty. Please note that a public offer of a reward is legally binding.

      Yeah, maybe in small claims court. And taking money to do someone’s homework if this is what it smells like is… Well, no penalties for you but the cat could maybe lose a course credit or even be expelled depending on school rules; paying you means his legal name is on record so… Enjoy your $20!

    A reply falls below the community's threshold of quality. You may see it by logging in.
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Newbie question under time constraint
by 1nickt (Canon) on Aug 04, 2019 at 22:56 UTC

    Hi, welcome to Perl, the One True Religion.

    Couple of etiquette notes: Asking for people to help you urgently because you are under a deadline usually has the opposite effect. Offering to pay for code here will usually get you sent to https://jobs.perl.org. But, effort is encouraged and you've shown plenty, good job getting as far as you have.

    Couple of general programming / asking for technical help notes:

    • When you don't know what is happening or why something isn't behaving as you expect, throw in a debug statement. It can be as simple as warn "foo $foo", or you can use a debugging module (the new Endoscope is very cool), or you can build for the future and set your app up from the start with logging built in by using a simple framework like Log::Any.

    • Add tests from the start as well. As you write your code, write a test suite that proves that when you feed the app some data, you'll get the expected result. Keep adding tests as you add features. Then when some new code breaks some old code, a test will tell you.

    • When you ask for help, it's most effective if you post sample data, expected output, and the error message encountered. Least effective are reports like "is not remotely working, the main program isn't even functional yet,". See many links in the documentation of the Monastery describing how best to ask a question. I had to guess what your data is like because you provided none.

    Regarding your program my advice is to use one of the many object frameworks Perl already has. Not only because the features that a good framework has that you are replicating by hand, are stable, tested, efficient, but also because a good OOP framework will allow you to expand your app into new levels of complexity as it grows while hiding the majority of the dirty work. Personally I never write anything OOP without Moo.

    Here's something like what you described. (Note only one file.)

    $ cat 11103867.pl
    package Employee { use Moo; # loads strict and warnings and provides new() has name => (is => 'ro'); has [qw/wage hours/] => (is => 'rw'); }; package Employees { use Employee; use Moo; use namespace::clean; has _db => ( # code can be swapped later when there is a real DB is => 'rwp', default => sub { +{} }, ); sub add { my $self = shift; my $args = shift; # argument validation needed here $self->_db->{ $args->{name} } = $args; } sub find { my $self = shift; my $name = shift; # argument validation needed here my $record = $self->_db->{ $name } or die "$name not found"; return Employee->new( name => $name, wage => $record->{wage}, hours => $record->{hours}, ); } sub avg_hourly_wage { my $self = shift; my ($total_hours, $total_wages); for my $name (keys %{ $self->_db }) { $total_hours += $self->_db->{ $name }{hours}; $total_wages += $self->_db->{ $name }{wage} * $self->_db-> +{ $name }{hours}; } return sprintf('%d.2', $total_wages / $total_hours); } sub pay_change_for { my $self = shift; my $employee = shift; my $new_wage = shift; $employee->wage($new_wage); $self->_db->{ $employee->name }->{wage} = $new_wage; } }; #------------------------------------# use strict; use warnings; use feature 'say'; use Employee; use Employees; my $employees = Employees->new; for my $line (<DATA>) { chomp $line; my ($name, $wage, $hours) = split /,/, $line; $employees->add({ name => $name, wage => $wage, hours => $hours, }); } say 'Query for an employee: '; chomp( my $name = <STDIN> ); my $employee = $employees->find($name); say sprintf('%s earns %s for %s hours', map { $employee->$_ } qw/name +wage hours/); say 'The average hourly wage overall is ' . $employees->avg_hourly_wag +e; say "Give $name a raise? Enter new wage or 'No'"; chomp( my $answer = <STDIN> ); if ($answer =~ /^\d+$/) { $employees->pay_change_for($employee, $answer); } else { say 'No change'; } say 'The average hourly wage overall is now ' . $employees->avg_hourly +_wage; __DATA__ Fred Flinstone,10,40 Barney Rubble,8,40 Dino Flintstone,15,40 Bam-Bam Rubble,5,12 Mr. Slate,65,32

    Output:

    $ perl 11103867.pl
    Query for an employee: Barney Rubble Barney Rubble earns 8 for 40 hours The average hourly wage overall is 21.2 Give Barney Rubble a raise? Enter new wage or 'No' 20 The average hourly wage overall is now 24.2

    Hope this helps!

    Update: added some more methods for fun


    The way forward always starts with a minimal test.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11103867]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (2)
As of 2024-04-26 03:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found