Having said that, I will leave it as an excercise for the reader to verify that my code, run under strict and -w, spews warnings and errors under almost every condition that you intend for your error tests to catch.
If someone passed 2.4 as the first argument to your routine (perhaps they calculated the value but forgot to round it)? Your code just goes ahead an uses that value (producing non-symmetric cycles), and the user of the routine has to figure out if the bug is in their code or yours (I'd be inclined to say both).
Another possibility is if I have a reference pointing to the value I wish to use, but forget to deref that variable in the call --- a reference in numeric context resolves to the address, which is likely to be so big that no cycling will occcur, but there will be no obvious indication why it's not cycling if I can't readily spot my blunder.
A last point is your code passes responsibility of dealing with 0 by just letting perl give an 'illegal division by zero' exception if I pass something that evaluates to zero as the first argument. This exception points to the line in the closure code, so I'll have to go and look there to find the operation in question and then backtrack to discover my calling error. Also, I won't get the error until I use the closure, though it might be nicer to know I have a bogus closure when I generate it.
None of this is to say that there are occasions were it's ok to not check for errors on system calls, or other external I/O. Paranoia about your outside environment is fundamental to good programming. But paranoia about the correct use of your own code isn't, and I dare say, it's even somewhat un-Perlish.
Sanity checks aren't about paranoia, they are about checking to see if everything makes sense to continue, and to provide information to the programmer/user if not. It is not a matter of being more or less Perlish. If I write a routine that multiplies two matrices, I'll do a check that they are row/column compatible rather than relying on perl to merely provide 'uninitialized' warnings to the user when things go out of bounds. Though, in such a case I'd probably issue a diagnostic warning and just return; to indicate failure. There are all kinds of useful sanity checks beyond external dependencies --- checking that input parameters are within allowable ranges is neither evil nor un-Perlish.
Your attitude appears to be that it is the responsibility of the programmer to call your routine correctly and they should have done any sanity checking necessary prior to the call (and why should you waste your time helping them if they didn't, right?). Ovid's attitude appears to be that parameter sanity checks belong inside the routine, and wants provide some diagnostically meaningful information if they fail (although, his error message was out of sync with his code, and as you point out, the array test could have been better).
Taking a quick stab at putting the two extremes into a module (note: I changed the spec to just accept a list rather than an array ref after the first argument, and other than changing Ovid's error checks and output, the routines are essentially as posted by both of you) we have:
package Cycle; use warnings; require Exporter; @ISA = qw/Exporter/; @EXPORT = qw/cycle MC_cycle/; use strict; use Carp; sub cycle { # based on Ovid's algorithm my $toggle = shift; my @items = @_; unless ($toggle =~ /^\d+$/ and $toggle != 0 and @items) { my $me = (caller(0))[3]; confess "Hey stupid, you are supposed to call me like this:\n" +, "\t$me(positive_integer, some_list)\nNot like this:\n" +; } my $count = 0; my $index = -1; return sub { $index++ if $count++ % $toggle == 0; $index = 0 if $index == @items; $items[$index]; } } sub MC_cycle { # based on MeowChow's algorithm my ($repeat, @items, $index) = @_; return sub { $items[ int ($index++ / $repeat) % @items ]; } } __END__
I find myself more in Ovid's camp and would prefer to see the latter of the following two error messages when I accidentally use something that evaluates to 0 as the first argument:
# case 1: using MC_cycle(0, @colors); [jandrew:~]$ perl foo.pl Illegal division by zero at Cycle.pm line 29. # case 2: using cycle(0, @colors); [jandrew:~]$ perl foo.pl Hey stupid, you are supposed to call me like this: Cycle::cycle(positive_integer, some_list) Not like this: Cycle::cycle(0, 'red', 'white', 'blue') called at foo.pl line +6
Personally, I'd probably issue a warning and just return; to indicate failure rather than raise an exception, but that's a whole different discussion.
In reply to (danger - Sanity checking not evil) Re(4): Simple Rotation
by danger
in thread Simple Rotation
by Ovid
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |