http://www.perlmonks.org?node_id=1007476

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

Oh wise seers of the semicolon, I come to you in great distress, for I cannot for the life of me figure out this PERL problem on my homework. I've been working at it for about 7 hours now. Obviously, I'm not hoping for the exact code laid out for me because I do want to feel victorious, and I don't want to cheat but there are some very specific things that I don't know how to do that I would enjoy some samples for. Below is the assignment:

A popular horoscope kind of thing is to get a person’s luck number fro +m his/her date of birth. This is accomplished by adding together all +the digits in the day, month, and year of the birthdate, then repeat +the process until there is a single digit, which will be in the range + of 1 to 9, inclusive. For example, if the date is 21-Nov-1995, the p +rocess is as follows: 21: 2 + 1 = 3 11: 1 + 1 = 2 1995: 1 + 9 + 9 + 5 = 24: 2 + 4 = 6 and then, 3 + 2 + 6 = 11: 1 + 1 = 2 Thus, the lucky number of this date is 2. Write a subroutine/function called ‘sumDigits’ that takes a whole numb +er as an argument, and returns the sum of the digits in its argument. Write a script that reads dates from an input file, computes the lucky + number for each, and writes those numbers to an output file and also + to the screen. The script must ask the user for input and output fil +e names, and allow the user to re-enter the output file name if it al +ready exists. The script reads the input file one line at a time, bre +aks-up the date into three parts (day, month, and year), converts the + month into a numeric value (1 through 12), calls the function ‘sumDi +gits’ at least four times to compute the lucky number corresponding t +o that date, and then writes the number to the output file and also t +o the screen. Include a loop in the script that allows the user to repeat this proce +ss as often as he/she wishes. Sample Input File (hp2I.dat): 21-Nov-1995 18-Jan-2001 12-Mar-1975 Sample input and output: The output of your program must exactly match + the format shown below. Programmer: Name of the programmer(s) Course: COSC 146, Fall 2012 Lab#: Home project 2 Due date: Dec. 6, 2012 Enter an input file name: hp2I.dat Enter an output file name: hp2O.dat 2 4 1 Do it again, yes (or no)? YES Enter an input file name: hp2I.dat Enter an output file name: hp2O.dat The output file already exists, try again: hp2O.dat The output file already exists, try again: temp.dat 2 4 1 Do it again, yes (or no)? No

Specifically it would be of great help to see examples of:

I'm sure there's more, but that is where I am stuck now. Here's my current mess:

#!/usr/bin/perl -w #This script pulls birthdays and determines their lucky number. use strict; use warnings; my $again; my $month_numeric; my $exists; my @numbers; print "Programmer: Shaun Fyall\n"; print "Course: COSC 146, Fall 2012\n"; print "Lab#: Home project 2\n"; print "Due date: 12/6/2012\n\n"; do { print "Enter an input filename: "; chomp(my $in = <STDIN>); print "Enter an output filename: "; chomp(my $out = <STDIN>); my $exists = 0; while ($exists == 0) { if (-e "$out") { print "The output file already exists, try again: "; chomp($out = <STDIN>) } else { $exists = 1; } } #Move from file to array open(INPUT, "<","$in") or die $!; my @data = <INPUT>; chomp(@data); # Convert month to number my %getmonthnum = qw( Jan 1 Feb 2 Mar 3 Apr 4 May 5 Jun 6 Jul 7 Aug 8 Sep 9 Oct 10 Nov 11 Dec 12); foreach my $val (@data) { my ($day, $month, $year) = split(/-/, $val); $month = $getmonthnum{$month}; $day = sumDigits ($day); $month = sumDigits ($month); $year = sumDigits ($year); print "$day, $month, $year"; } print "\nDo it again? (yes or no): "; chomp($again = lc <STDIN>); }while ($again eq "yes"); sub sumDigits { my $num = @_[0]; my $sum = 0; do { $sum += $num % 10; $num = int($num / 10); # if ($sum > 9) # { # my $num2 = $sum; # my $sum2 = 0; # do # { # $sum += $num % 10; # $num = int($num / 10); # }while ($num != 0); # $num = 0; # $sum = $sum2; # } }while ($num != 0); return $sum; }

Replies are listed 'Best First'.
Re: Manipulating data from a file
by tobyink (Canon) on Dec 06, 2012 at 09:01 UTC

    The maths part isn't especially tricky...

    use 5.010; use strict; use warnings; # This expression is used to test if a string consists of a single # digit. We use it a couple of different places, so we'll just define # it once as a constant. # use constant DIGIT => qr/^[0-9]$/; # Let's assume you already have the date in an all numeric format. # say digit_sum('01-06-1980'); # This is the function which adds digits # sub digit_sum { my $string = shift; # First, split into digits. my @digits = grep { $_ =~ DIGIT } # keep only the numeric characters split '', $string; # split into characters # Add the digits together. my $sum = 0; $sum += $_ for @digits; # Handle the trivial case. # If $sum is just a single digit, return it as-is. return $sum if $sum =~ DIGIT; # Otherwise, recurse. return digit_sum($sum); }
    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: Manipulating data from a file
by bitingduck (Chaplain) on Dec 06, 2012 at 08:10 UTC

    It looks like you're doing pretty well on your own just from seeing the code change over a few minutes. It's not obvious that you need to extract array elements one by one. There are examples of how to pass parameters to subroutines here: perlsub. The math you have to do is just adding digits-- the trickier part is separating the digits, but you already know how to do that (though you may not know it). You can add them in a loop once they're split.

    One suggestion I'd make that you probably won't catch on your own this time through, is that you should make your hash all one case and force the month name to that case for comparison. That way the input can be 'Mar', 'MAR', 'MaR', or any other case combination.

    You might also rewrite how you read the file-- it looks from the assignment like you're supposed to do it a line at a time instead of grabbing all the lines at once into an array. A problem with perl is there's more than one way to do everything.

    And just to help people who are going to add comments, you should put in a note like "edit: (stuff that I changed)" when you edit it. When you're changing the code, just add a little comment.

Re: Manipulating data from a file
by RichardK (Parson) on Dec 06, 2012 at 11:52 UTC

    I think it's easier to use maths to calculate the sum of the digits of an integer.

    As this is your homework I'm not going to post the code but it's pretty simple using a while loop, the modulo operator % and int.