approach to calculating pay

C

ccc31807

This isn't strictly Perl question, but I've implemented this in Perl, and will re-implement it in Perl.

I have several contracts to build about five times a year. Among other provisions, the 'contract price' is one of the provisions. While some are negotiated (and therefore beyond my ken) most can be calculated. I have built a function to calculate the pay, to which I pass about nine different parameters, and it returns a discrete integer, the 'contract price.' Rather than describe it, there is some semi-pseudo code below. As you will note, it consists of a large number (almost 100) of if-elsif statements.

Question: can someone recommend a better approach?

Thanks, CC.

my %contracts = get_contract_info_from_database();
foreach my $contract (keys %contracts)
{
#initialize about 20 different variables that constitute the contract terms
my ($site, $rank, $experience, $ftptstat, $jobtype, $level ... ) = parse_row_from_hash($contracts{$contract);
$contract_price = calculate_contract_price($site, $rank, $experience, $ftptstat, $jobtype, $level ...);
#build and print each contract
}

sub calculate_contract_price
{
my ($site, $rank, $experience, $ftptstat, $jobtype, $level ... ) = @_;
my $contract_price = 0;
if ($jobtype eq 'a')
{
if ($level == 1)
{
if ($rank eq 'A')
{
# etc for the rest of the nine parameters until, at the bottom, I have this:
# $contract_price = $hours * $production * 1288;
# (1288 constitutes the approved price for the work to be performed at the
# particular level, rank, site, time status, job type, etc.
}
elsif ($rank eq 'B')
{

}
elsif ($rank eq 'C')
{

}
else { warm "ERROR in rank, $rank\n"; }
}
elsif ($level == 2)
{

}
elsif ($level == 3)
{

}
else { warn "ERROR in level: $level\n"; }
}
elsif ($jobtype eq 'b')
{

}
elsif ($jobtype eq 'b')
{

}
else { warn "ERROR in jobtype: $jobtype\n";
return $contract_price;
}
 
P

Peter J. Holzer

This isn't strictly Perl question, but I've implemented this in Perl, and will re-implement it in Perl.

I have several contracts to build about five times a year. Among other provisions, the 'contract price' is one of the provisions. While some are negotiated (and therefore beyond my ken) most can be calculated. I have built a function to calculate the pay, to which I pass about nine different parameters, and it returns a discrete integer, the 'contract price.' Rather than describe it, there is some semi-pseudo code below. As you will note, it consists of a large number (almost 100) of if-elsif statements.

Please keep your lines at about 72 characters. Long lines are hard to
read.


Question: can someone recommend a better approach?

Thanks, CC.

my %contracts = get_contract_info_from_database();
foreach my $contract (keys %contracts)
{
#initialize about 20 different variables that constitute the contract terms
my ($site, $rank, $experience, $ftptstat, $jobtype, $level ... ) = parse_row_from_hash($contracts{$contract);
$contract_price = calculate_contract_price($site, $rank, $experience, $ftptstat, $jobtype, $level ...);
#build and print each contract
}

sub calculate_contract_price
{
my ($site, $rank, $experience, $ftptstat, $jobtype, $level ... ) = @_;
my $contract_price = 0;
if ($jobtype eq 'a')
{
if ($level == 1)
{
if ($rank eq 'A')
{
# etc for the rest of the nine parameters until, at the bottom, I have this:
# $contract_price = $hours * $production * 1288;
# (1288 constitutes the approved price for the work to be performed at the
# particular level, rank, site, time status, job type, etc.
}

One obvious solution is a table, possibly with wildcards if not all
combinations are different:

my @rules =
(
{
site => 's1',
rank => 'A'
experience => 'high',
ftptstat => '*', # don't care
jobtype => 'a',
level => 1,
...
price => 1288.
}
);

sub calculate_contract_price {
my ($site, $rank, $experience, $ftptstat, $jobtype, $level ... ) = @_;

for my $rule (@rules) {
if (($rule->{site} eq '*' || $site eq $rule->{site}) &&
($rule->{rank} eq '*' || $rank eq $rule->{rank}) &&
($rule->{experience} eq '*' || $experience eq $rule->{experience}) &&
...
) {
return $hours * $production * $rule->{price};
}
}
}

Of course doing the same check on many variables implies that they
should be put into a common data structure, e.g. a hash:

sub calculate_contract_price {
my ($param) = @_;

RULE: for my $rule (@rules) {
for my $p (keys $param) {
next RULE unless ($rule->{$p} eq '*' || $param->{$p} eq $rule->{$p};
}
return $hours * $production * $rule->{price};
}
}

(WARNING: Untested code)

Of course that table can also be stored in a database, which means that
a non-programmer could edit it.

hp
 
C

ccc31807

Please keep your lines at about 72 characters. Long lines are hard to
read.

Yes, I'll try.
One obvious solution is a table, possibly with wildcards if not all
combinations are different:

One problem is that I have a flat algorithm for one kind of job (method) and a decision tree for another method of job, and I'm trying to integrate the approach. Another problem is that the data has a lot of errors, and I need to be able to dynamically test the data. Running it through some kind of cleaning up routine is more trouble than it's worth, since I just throw thebad data away, i.e., the contract is trashed. Still another problem is that some kinds of jobs have the metric built in, while others are a multiple of some metric.

But ... you have given me an idea which might work. I'll build two hashes, one for the piece work and the other for the flat rate, that might look like below. I can then freeze the hash and load it every time I call it, or perhaps put it into a flat file and reload the hash every time.

my (%pieces, %flatrate);

$pieces{level1}{experience1}{certification1}{size1} = 672;
$pieces{level1}{experience1}{certification1}{size2} = 1524;
$pieces{level1}{experience1}{certification1}{size3} = 2286;
....
$pieces{level2}{experience2}{certification2}{size1} = 927;
$pieces{level2}{experience2}{certification2}{size2} = 1854;
$pieces{level2}{experience2}{certification2}{size3} = 2781;

$flatrate{level1}{size1} = 125;
$flatrate{level1}{size2} = 100;
$flatrate{level1}{size3} = 50;
$flatrate{level2}{size3} = 200;
....

A big part of finding a solution to a problem is being able to articulate it to another person. I find that explaining it clearly so that another person can understand the problem forces me to clarify it in my own mind, and some times the solution is self evident.

I appreciate your help. I won't work on this until October, but I'll remember to update this thread with the results.

CC.
 
U

Uri Guttman

c> my (%pieces, %flatrate);

c> $pieces{level1}{experience1}{certification1}{size1} = 672;
c> $pieces{level1}{experience1}{certification1}{size2} = 1524;
c> $pieces{level1}{experience1}{certification1}{size3} = 2286;

OOOOF!!

$pieces{level1}{experience1}{certification1} = {
size1 => 672,
size2 => 1524,
size3 => 2286,
} ;

learn to remove redundancy. in the above case it is faster, easier to
write and much easier to read.

you can make a nested data tree for the keys in there as well. if it
gets too deep and complex, assign some of the lower nodes into scalars
and assign those into the parent tree. this is basic perl data
structures. you should never see lines with those dup hash lookups in
each one.

uri
 
R

Rainer Weikusat

ccc31807 said:
This isn't strictly Perl question, but I've implemented this in Perl, and will re-implement it in Perl.

I have several contracts to build about five times a year. Among other provisions, the 'contract price' is one of the provisions. While some are negotiated (and therefore beyond my ken) most can be calculated. I have built a function to calculate the pay, to which I pass about nine different parameters, and it returns a discrete integer, the 'contract price.' Rather than describe it, there is some semi-pseudo code below. As you will note, it consists of a large number (almost 100) of if-elsif statements.

Question: can someone recommend a better approach?

Thanks, CC.

my %contracts = get_contract_info_from_database();
foreach my $contract (keys %contracts)
{
#initialize about 20 different variables that constitute the contract terms
my ($site, $rank, $experience, $ftptstat, $jobtype, $level ... ) = parse_row_from_hash($contracts{$contract);
$contract_price = calculate_contract_price($site, $rank, $experience, $ftptstat, $jobtype, $level ...);
#build and print each contract
}

sub calculate_contract_price
{
my ($site, $rank, $experience, $ftptstat, $jobtype, $level ... ) = @_;
my $contract_price = 0;
if ($jobtype eq 'a')
{
if ($level == 1)
{
if ($rank eq 'A')
{
# etc for the rest of the nine parameters until, at the bottom, I have this:
# $contract_price = $hours * $production * 1288;
# (1288 constitutes the approved price for the work to be performed at the
# particular level, rank, site, time status, job type, etc.
}
elsif ($rank eq 'B')
{

}
elsif ($rank eq 'C')
{

}
else { warm "ERROR in rank, $rank\n"; }
}

[...]

Hmm .... "don't make such mess of it next time"?. I think it is highly
unlikekly that you really need an orthognal 9D-space to classify the
type of contracts you have to deal with as this would either mean an
enormous amount of contracts or pretty much 'no classification at
all'. If you can express some of your input variables as functions of
some of the other input variables or omit them altogether, the problem
itself would become much simpler.

Apart from that, the 'hash idea' is generally sensible. A useful
simplication could be to create a 'compound key' from your input
values by joining them together with some 'neutral' character. Perl
has some builtin support for this (see description of $; in 'perldoc
pervar') but I think creating real 'compound keys' would be a better
choice. The set of 'valid contracts' would then exist in a single
hash and the if - cascades would be replaced by joining the input
values and doing a lookup in this hash.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top