Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Common Beginner Mistakes

by chromatic (Archbishop)
on Dec 15, 2000 at 04:40 UTC ( [id://46769]=perltutorial: print w/replies, xml ) Need Help??

Beginning programmers face a great learning curve. Beyond the syntax of their starting language, they must master the concepts of algorithms, data structures, and the idioms used to accomplish common tasks. Most of the books in my library concentrate on grammar, syntax, and standard libraries, while glossing over good practices and common mistakes.

Perl has its own unique set of gotchas and strengths. Some make it easy for beginners to hang themselves, and others could reduce the amount of code and heartache needed. (Perl is fairly unique among languages in that it often lets beginners and dabblers succeed despite their lack of formal training.)

Without further ado, here are Common Mistakes of Beginning Programmers (part one).

Variable Variable Names

The mistake:

Beginners will often do something like this:
foreach ($i = 1; $i < 10; $i++) { ${"record$i"} = $i * $i; }

Why it's a mistake:

Symbolic references are a well documented pitfall. Besides that, Perl has built-in and easy-to-use aggregate data types. That means that it makes a distinction between a thing and a pile of things. In addition, you will need to keep track of the index variable $i to know how many records you have.

Why they do it:

Beginning programs are often unaware of genericity and aggregation. They like naming variables, and don't realize yet that you don't have to name everything that's ever created.

The solution:

Use an array or hash (the aggregate data type) to hold the records. Not only will Perl keep track of the number of records, it's easier to do common operations on them.
for (1 .. 10) { push @records, $_ * $_; } foreach my $record (@records) { print $record, "\n"; }

Repetition without Abstraction

The mistake:

my $var1 = $query->param('var1'); my $var2 = $query->param('var2'); my $var3 = $query->param('var3'); my $var4 = $query->param('var4'); my $var5 = $query->param('var5'); my $var6 = $query->param('var6'); my $var7 = $query->param('var7'); my $var8 = $query->param('var8'); my $var9 = $query->param('var9'); my $var10 = $query->param('var10');
Yikes. At least he used CGI.pm, anyway. Here's another example:
$report = generate_report($foo); open(FILE, ">>report.txt") or die "blah!"; print FILE "Report number: $i"; $i++; # # three lines to format header nicely # @report_lines = split(/\n/, $report); print FILE $report_lines[0]; print FILE $report_lines[1]; print FILE $report_lines[2]; print FILE $report_lines[3]; print FILE $report_lines[4]; close FILE or die "blergh!"; # skip a few $report = generate_report($bar); open(FILE, ">>report.txt") or die "blah!"; print FILE "Report number: $i"; $i++; # # format the header again # @report_lines = split(/\n/, $report); print FILE $report_lines[0]; # you see where this is going

Why it's a mistake:

Computers are supposed to make our lives easier. Anonymous data structures, objects, loops, modules, macros, and templates are supposed to save effort. Why do people not get beyond cutting and pasting?

Problems arise, as they always do, when a throwaway program becomes important enough to keep around. Those programs have a way of attracting deadlines -- you'll have to add something, but you won't have time to do it right. Repeat that another way. There's no better time to do a program right than before anyone's depending on it.

Why they do it:

Designing things that have to work just once is easy. Designing things that have to work a few different ways is difficult. Designing things that are flexible, powerful, and easily understood is even worse. A few extra minutes of typing seems like a small price to pay for not having to think too hard under a time crunch.

The solution:

If you find yourself duplicating functionality in different places, stop. Factor out the common elements and turn the variables into variables. Sometimes this means you've been trying the wrong approach. Other times, you're not approaching the problem the right way.

A literal translation of the first, somewhat better:

my %vars; for (1 .. 10) { $vars{"var$_"} = $query->param("var$_"); }
A whole lot better:
# change the form to use multiple selections with the same name my @vars = $query->param('var'); # valid if you have a combobox or checkbox group
The second is lots nicer:
{ my $num_displayed = 0; sub display_report { my $data = shift; my $report = generate_report($data); open(FILE, ">>report.txt") or die "Can't append to report.txt: $!" +; print FILE "Report number: $num_displayed"; $num_displayed++; print FILE $report; } } # end of enclosing scope for $num_displayed
Some items lend themselves to functions more than others. As you practice, you will improve at figuring out which things are important and which are variable.

Fear of Documentation

The mistake:

Not typing 'perldoc perltoc'.

Why it's a mistake:

The Camel has a list price of US $49.95. Getting answers from comp.lang.perl.misc, PerlMonks, and #Perl cost time and sometimes dignity. If you have access to a computer with Perl on it, you have access to the documentation -- the source material from which much of the Camel springs. (If you don't have the documentation, you have a more serious problem called "BoFH infestation". You'll have to fumigate.)

Why they do it:

Some people don't know the documentation is there. A careful nudge "Hey, it's under the ActiveState program group or available by typing perldoc perltoc and browsing for a while" is all that's needed. Others don't have the confidence in their own abilities or the laziness necessary to motivate themselves into being able to answer their own questions.

The solution:

Read the FAQ (perldoc perlfaq). Read perldoc perldoc. Learn how to search for builtin function docs (perldoc -f function). Learn how to search the FAQ by keyword (perldoc -q keyword). If you come across a word you don't know, see perldoc perltoc. Yes, it takes a while. So does learning to be a basketball player, a Bolivian cat herder, or an astroman. But the documentation is always there, even when we're not. When you don't have time to wait for a response from Usenet or the web, and when you're not in the mood to placate the IRC Ops, you'll be glad you know how to search your own hard drive for the answers.

Update: Fixed an unintentional typo in the first beginner code, thanks to turnstep. chipmunk is right... I'm open to writing a second-parter, so send me your suggestions. Fixed a second unintentional typo in the second example, thanks to erix.

Replies are listed 'Best First'.
Re: Common Beginner Mistakes
by Dominus (Parson) on Dec 15, 2000 at 09:38 UTC
Re: Common Beginner Mistakes
by japhy (Canon) on Dec 15, 2000 at 09:31 UTC
Re: Common Beginner Mistakes
by chipmunk (Parson) on Dec 15, 2000 at 04:54 UTC
    Lots of good observations and advice, but in a post on common beginner mistakes, I'm surprised you didn't link to the perltrap documentation. :)
Re: Common Beginner Mistakes
by Elias (Pilgrim) on Jan 27, 2001 at 16:15 UTC

    As a beginner, I made all of these mistakes and then some. Thank you chromatic for this excelent node, and dominus for the Red Flag papers. While forcing myself to rewrite a script to have them all  use strict I came up with the following solutions which may be of benefit to others like me (for most of you this is way too trivial, but this is the voice of a beginner).

    To access a number of files in a loop construct, this was my initial attempt:

    for ($results = 2; $results <34; $results++) { my $file="current$results.res"; open (TALLYRESULTS, ">$file") or die ("cant open results f +ile"); .... }

    A common mistake, apparently, which turned out to be easy to avoid:

    for ($results = 2; $results <34; $results++) { my $file="current".$presence.".res"; .... }

    Another (double!) 'Red Flag' situation was this:

    @tree1=@samples[0..2]; ... (another 10 of those) @tree12=@samples[33..35]; for ($z = 1; $z <= 12; $z++) { $arrayname="tree$z"; foreach $patch(@$arrayname) { ... do something }

    and solving it taught me a thing or two about hashes:

    my %trees = (1 => "@samples[0..2]", ... (another 10 of these) 12 => "@samples[33..35]", ); while ((my $tree, my $samples) = each (%trees) ){ my @patches = split (/ */, $samples); foreach $patch(@patches) { ... do something }

    My script now runs under strict, and I'm proud of it ;-)

Re: Common Beginner Mistakes
by mrmick (Curate) on Dec 18, 2000 at 21:26 UTC
    Excellent article!!

    Although these are typical of 'beginners' , I would like/not like to point out that I've seen some 'veterans' who tend to make the same mistakes.

    I'm not entirely without guilt, either. I recently took a look at some 'old' code (developed in '97 when I was starting to learn Perl) and saw some wonderful examples of what you have pointed out above.

    Mick
Re: Common Beginner Mistakes
by merlyn (Sage) on Dec 18, 2000 at 01:26 UTC
Re: Common Beginner Mistakes
by Mabooka-Mabooka (Sexton) on Apr 17, 2005 at 03:39 UTC
    I think this article is an excellent *stub* for the theme:-). "Part one" sounds very encouraging though.
    I'd be pleased to see a much bigger (and preferably categorized) list of common beginner's pitfalls. It would be only fare to point novice Perl developers to such list *before* they even start developing (maybe it'd force some not to start with Perl at all afterwords; but it's fair, isnt' it?..:-)). It's hard to overestimate a value of such a list, if it were comprehensive and systematical.

    Please don't misinterpret me: although i don't normally use Perl for developing and it usually makes me very unhappy, I am not trying to start yet another "Horses Vs. Cows" war here:). My point is: all beginners make and will make tons of same mistakes; people should know what they are about to face and be aware that learning Perl to a dergee of not making these mistakes anymore is a comparatively big learning curve.

    Out of the three mistakes described here in the article, I think only 1st one really belongs to the "Common *Perl* mistakes" category.
    2) Repetition without Abstraction: it's a generic ugly coding practice problem; I don't see what it has to do with Perl. I mean, people who write like this will write like this in any language; and vice versa: people who don't in other langauage, won't in Perl either.
    3) is common sense.

    On the web, I also often see Perl idioms / general good practices mixed with "beginner's mistakes". Things like "Use "open or die" instead of "open"", "don't forget to print Content-type..." and xillions of others that have nothing to do with Perl.

    But enough philosophyzing. I will now add a couple examples -- things that I stepped onto in my own practice -- as separate posts. That is, if I'm not moderated out of here...:-)

      sub()

      This one was the reason I came here, details are in the Seekers of Perl Wisdom thread. (THANKS again BUU!!!).
      In short: if you write
      sub foo() { ... },
      you're in trouble.

      Why people do this?

      Mainly (I think) because it's counter-intuitive to do otherwise. I personally automatically without thinking add "()" to a function declaration. Besides, I call it as "foo()".

      Another reasons: Perl allows you to do that. What I encountered is, depending on where one calls sush sub, -- before or after it's defInition(!!!), there'll be a syntax error or no errors. (In fact, after fixing my small problem today, I looked closer at my colleague's module and found several dozen subs declared like this. No complaints from Perl).

      Solution

      Read all books, spend several years practicing. I leave to the experts the explanation of what "sub name()" really means (it's an array of subs or smth. like that I guess).
      -- Find out.

      Perl allows to use arithmetics on string

      Here's the code:
      #!/usr/local/bin/perl -w use strict; sub step_over_desert # () I won't repeat this mistake anymore!!! { print "Tsock...\n"; } sub cover_the_distance # () I won't repeat this mistake anymore!!! { my ($me, $desert_width, $max_spit_distance, $num_legs, $last_name, + $day_phone, $days_without_water) = @_; my $num_steps=0; while($desert_width) { my $todo = $desert_width * $num_legs; print "$desert_width miles remaining; $todo steps yet to do.\n" +; step_over_desert(); $desert_width--; $num_steps += $num_legs; } print "Steps made: $num_steps\n"; } #cover_the_distance("strong animal", 5, 15, 4, "Mabooka", "(123)-456-7 +890", -1); cover_the_distance("strong animal", "five thousand miles", 15, 4, "Mab +ooka", "(123)-456-7890", -1); 1;

      Don't try it at home

      Well, I recommend at the very least to redirect the output to a file:-).

      Why people do this?

      Again, the main reason: because it allows you. One may expect either a compile-time error or a run-time exception. But Perl allows you to shoot your own foot.
      Actually, this is an obfuscated (starting to love the word) code from a real-life app. The real application would send a query over the wire to the server,
      get a responce line --
      -- that should look smth. like this:
      200 OK 578
      -- meaning " there're 578 lines yet to read from the socket,
      parse this line and create a loop:
      while($numlines) { get_next_one(...); $numlines--; ... }
      Well guess what? Once in 10000 times the server would get tired and send a garbled status line -- smth. like:
      200 OK whatever,man
      and
      (while "whatever,man"--) {....}
      will start the almost infinite loop. (Try the code above).

      Solution

      Be aware.
      use int($n).
      Ask your new hires during the inteview: "What is the result of the following operation:
      "Larry"--;
      -?.

      One would believe it fails... it doesn't

      It tries to do the right thing. The result in general is unpredictable. (That is, for a beginner).
      The snippet is:
      #! /usr/local/bin/perl use strict; sub print_percentage { my ($total, $part) = @_; if (int($total) != 0){ printf ("It's about %.2f%%\n", $part/$total *100.); } else{ print "Bad usage: $total == 0!!! Cannot divide by '$total'.\n" +; } } print_percentage(100, 50); print_percentage("oops", 50); ;1
      One could write many variations on the theme:
      #! /usr/local/bin/perl use strict; if ("38" == 38){ print "hit the jackpot!\n"; } else { print "hit elsewhere....\n"; } ;1

      Why people do this?

      People do mistakes, and they are spoiled by getting used to compilers' help. Many who come to Perl from other languages are used to "it doesn't make any sense" compiler's error. Or run-time exceptions. They are NOT used to the machine "doing the right thing" for them (of course! what do you mean by doing the right thing?) And that's the problem for beginners.

      Solution

      Set up your mind not to guess what it'll do. Learn, find out, know.
Re: Common Beginner Mistakes
by royalanjr (Chaplain) on Dec 21, 2000 at 20:14 UTC
    I'll have to post this next to the monitor and have my coworkers slap me if I do anything you mention in the article.

    Great post. Thanks!

    Roy Alan

Re: Common Beginner Mistakes
by Anonymous Monk on Jul 17, 2002 at 20:28 UTC
    Please, to excuse my ignorance. But, could someone tell me what BoFH means?
      Ever heard of Google?

      cLive ;-)

      --
      seek(JOB,$$LA,0);

      "Bastard Operator from Hell"

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (11)
As of 2024-03-28 09:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found