Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Birthday List

by Drgan (Beadle)
on Aug 28, 2003 at 13:14 UTC ( [id://287347]=perlcraft: print w/replies, xml ) Need Help??

   1: #!/usr/bin/perl -w
   2: #####################################################################
   3: # This is a test program to start work on building up some real     #
   4: # skill with Perl. Also, must remember to refer to Perl as Perl and #
   5: # not PERL. Someone might get offended.                             #
   6: # The purpose of this program is to create and read a small         #
   7: # flat-file database of birthdays to remember. Boring but useful.   #
   8: # Please be kind to me, this is my first Perl application and I     #
   9: # was hoping to get some feedback. Maybe the community can tell me  #
  10: # If I'm starting out good? So many of these ideas are still new to #
  11: # me because I'm so used to PHP's style.                            #
  12: #####################################################################
  13: 
  14: use strict;								# Enforce legal variables.
  15: 
  16: my ($choice);							# The user's choice.
  17: my ($name);								# Person's name
  18: my ($date);								# Birthday
  19: my (@birthdays);						# List of Birthdays.
  20: my ($key);
  21: 
  22: while () {
  23: 	print "(L)ist or (N)ew or (M)odify birthday? "; chomp($choice = <STDIN>);
  24: 	if ($choice =~ /l/i) {
  25: 	# User picked List
  26: 		open (IN, "birthdays.db") || die "Failure: $!";
  27: 		while (<IN>) {
  28: 			print;
  29: 		} # Print it all to the screen
  30: 		close (IN);
  31: 	} elsif ($choice =~ /n/i) {
  32: 		open (OUT, ">>birthdays.db") || die"Failure: $!";
  33: 
  34: 		print "Enter person's name: "; chomp ($name=<STDIN>);
  35: 		print "Enter person's birthday: "; chomp ($date = <STDIN>);
  36: 		print OUT "$name:$date\n";
  37: 
  38: 		close (OUT);
  39: 	} elsif ($choice =~ /m/i) {
  40: 		open (IN, "birthdays.db");
  41: 		push @birthdays, $_ while (<IN>);
  42: 		close (IN);
  43: 		$key = 0;
  44: 		foreach (@birthdays) {
  45: 			print "$key: $birthdays[$key]";
  46: 			$key++;
  47: 		} # Store file information in memory.
  48: 		$key = "";
  49: 		print "Enter record to modify: "; chomp ($key = <STDIN>);
  50: 		print "(M)odify or (D)elete? "; chomp ($choice = <STDIN>);
  51: 		open (OUT, ">birthdays.db");
  52: 		$birthdays[$key] = "" if ($choice =~ /d/i);
  53: 		print OUT @birthdays;
  54: 		if ($choice =~ /m/i) {
  55: 			print "Enter person's name: "; chomp ($name=<STDIN>);
  56: 			print "Enter person's birthday: "; chomp ($date = <STDIN>);
  57: 			$birthdays[$key] = "$name:$date\n";
  58: 			print OUT @birthdays;
  59: 		} # put it all back in the file.
  60: 		close (OUT);
  61: 		@birthdays = (); # Clear that annoying array. It causes problems if we don't.
  62: 	}
  63: }

Replies are listed 'Best First'.
Re: Birthday List
by dragonchild (Archbishop) on Aug 28, 2003 at 13:41 UTC
    A few suggestions:
    1. Don't pre-declare your variables. Only declare them as you need them. In general, the only reason to declare a variable outside the logic structure within which it is set is if you're going to use it outside that logic structure. As you never use any of these variables outside the loop, declare them inside. :-) (That will also remove the need to Clear that annoying array.)
    2. Don't do if ($choice =~ /l/i). Instead, do
      $choice = lc $choice; # ... later ... if ($choice eq 'l')
      It makes your intent clearer. (That it's also more efficient is a bonus.)
    3. The code for your user-picked list is interesting. I would have written it as:
      my $filename = 'birthday.db'; # ... later ... open IN, $filename || die "Cannot read '$filename': $!\n"; print <IN>; close IN;
      Then, I realized that there is a perfectly valid reason to do it your way - if the file is really large. You chose the memory-light method. I chose the coding-light method. *shrugs* TMTOWDI.
    4. The last point illustrated something - anytime you have a constant, you should look at defining it up above. That way, you only define it once, making it easier to change. Also, your code is easier to read. So, I would rewrite another portion as such:
      use constant CHOICE_LIST => 'l'; my $db_file = 'birthday.db'; # ... later ... if ($choice eq CHOICE_LIST) { open(IN, $db_file) || die "Cannot read '$db_file': $!\n"; print <IN>; close IN; }
    5. In your add-an-entry code, I would add some validation checks. Just to make sure that stuff in your database looks good.
    6. Your modify/delete code is very ... convoluted. I'd do the following:
      if ($choice eq CHOICE_MODIFY) { # Use a hash, not an array. The assumption here is that the names +will be unique. open(IN, $db_file) || die "Cannot read '$db_file': $!\n"; my %birthdays = map { chomp; split ':' } <IN>; close IN; # Print out a list of the names and birthdays print "$_ => $birthdays{$_}\n" for sort keys %birthdays; print "Enter record to modify: "; chomp ($key = <STDIN>); print "(M)odify or (D)elete? "; chomp ($choice = <STDIN>); $choice = lc $choice; if ($choice eq 'm') { print "Enter person's birthday: "; chomp ($date = <STDIN>); $birthdays{$key} = $date; } elsif ($choice eq 'd') { delete $birthdays{$key}; } open OUT, $db_file || die "Cannot overwrite '$db_file': $!\n"; print OUT "$_:$birthdays{$_}\n" for keys %birthdays; close OUT; }
      This way, you re-use as many lines of code as possible.
    7. I am impressed with your avoidance of a lot of common newbie pitfalls, such as not checking the results of open and the like.

    ------
    We are the carpenters and bricklayers of the Information Age.

    The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      open(IN, $db_file) || die "Cannot read '$db_file': $!\n"; my %birthdays = map { chomp; split ':' } <IN>; close IN;
      It took me looking back through my book to understand exactly how that works;however, I still have one question about that. I want to create a key that's not the name or the birthday. I'm aware of people with the exact same name so having their names being the key to access them by would be a bad thing. Now, I'm not into accessing an actual database yet but from what I'm aware of through using PHP, an array of associative arrays would help to solve the problem of creating the extra key. Unfortunately oh great one, I'm not sure that I comprehend doing this in Perl.
      "I have said, Ye are gods; and all of you are children of the most High." - Psalms 82:6
        You're right - same names can cause a problem and having a unique id would solve it. What you end up having to do is have the key of the associative array (also known as a hash) be the unique ID (which doesn't necessarily have to be a number, though usually is) and the value be a reference to the data structure for that unique id.

        However, like all solutions, I would make sure that you actually have the problem before implementing a solution that causes your code to become more complex. If you had two Jim Jones, I'd make sure to change it to Jim A. Jones and Jim B. Jones. Remember, you have to relate this back to your own life. So, something like "Jim Jones (college)" and "Jim Jones (work)" can also be used without having to change the code. :-)

        ------
        We are the carpenters and bricklayers of the Information Age.

        The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

        Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: Birthday List
by TomDLux (Vicar) on Aug 28, 2003 at 18:05 UTC

    Consider using Shell::POSIX::Select for the menus. For the price of a slight complication in code, you obtain robustness and greater user control.

    Documenting variable declarations sounds good, and at one time was presented as an important software engineering technique. But look at your documentation:

    my ($choice); # The user's choice. my ($name); # Person's name my ($date); # Birthday my (@birthdays); # List of Birthdays. my ($key);

    Does "The user's choice" convey any more information than does "$choice"? or "Person's name" compared to "$name"? The one comment which clarifies the variable name is "$date" ... "Birthday", which leads me to suggest the variable name should be changed to "$Birthday". If you have comments, they should be more informative. As well, any information in comments appears once, at the declaration; any information in the variable name appears every time it is used.

    Part of the reason C/Pascal/Ada programmers started using comments at the declaration is because declarations appeared at the top of the routine, far from the use. Variables $name and $date are only used at lines 33, 34, 35 and lines 54, 55, 56; @birthdays is only used lines 40-60. Declaring @birthdays at line 40, declaring $name and $date at line 33 and at line 54 means the names are totally sufficient documentation. As well, local declarations guarantee you don't have to worry about the variables being used elsewhere, simplifying comprehension.

    My web site has an article on Coding Style, with links to stuff by Kernighan, Pike, chromatic, Mark-Jason, and many others. It makes interesting reading!

    --
    TTTATCGGTCGTTATATAGATGTTTGCA

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (6)
As of 2024-04-16 09:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found