Remove switch from perltodo
[p5sagit/p5-mst-13.2.git] / pod / perlfaq4.pod
index 67ab210..a8d7dad 100644 (file)
@@ -1,6 +1,6 @@
 =head1 NAME
 
-perlfaq4 - Data Manipulation ($Revision: 1.64 $, $Date: 2005/04/27 00:18:04 $)
+perlfaq4 - Data Manipulation ($Revision: 1.71 $, $Date: 2005/11/23 07:46:45 $)
 
 =head1 DESCRIPTION
 
@@ -349,16 +349,16 @@ Computers are good at being predictable and bad at being random
 (despite appearances caused by bugs in your programs :-).  see the
 F<random> article in the "Far More Than You Ever Wanted To Know"
 collection in http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz , courtesy of
-Tom Phoenix, talks more about this.  John von Neumann said, ``Anyone
+Tom Phoenix, talks more about this.  John von Neumann said, "Anyone
 who attempts to generate random numbers by deterministic means is, of
-course, living in a state of sin.''
+course, living in a state of sin."
 
 If you want numbers that are more random than C<rand> with C<srand>
 provides, you should also check out the Math::TrulyRandom module from
 CPAN.  It uses the imperfections in your system's timer to generate
 random numbers, but this takes quite a while.  If you want a better
 pseudorandom generator than comes with your operating system, look at
-``Numerical Recipes in C'' at http://www.nr.com/ .
+"Numerical Recipes in C" at http://www.nr.com/ .
 
 =head2 How do I get a random number between X and Y?
 
@@ -392,7 +392,7 @@ integers (inclusive), For example: C<random_int_in(50,120)>.
 The localtime function returns the day of the year.  Without an
 argument localtime uses the current time.
 
-    $day_of_year = (localtime)[7];
+       $day_of_year = (localtime)[7];
 
 The POSIX module can also format a date as the day of the year or
 week of the year.
@@ -409,7 +409,7 @@ a time in epoch seconds for the argument to localtime.
        my $week_of_year = strftime "%W",
                localtime( timelocal( 0, 0, 0, 18, 11, 1987 ) );
 
-The Date::Calc module provides two functions for to calculate these.
+The Date::Calc module provides two functions to calculate these.
 
        use Date::Calc;
        my $day_of_year  = Day_of_Year(  1987, 12, 18 );
@@ -436,15 +436,12 @@ reliably determine the current century or millennium.
 
 =head2 How can I compare two dates and find the difference?
 
-If you're storing your dates as epoch seconds then simply subtract one
-from the other.  If you've got a structured date (distinct year, day,
-month, hour, minute, seconds values), then for reasons of accessibility,
-simplicity, and efficiency, merely use either timelocal or timegm (from
-the Time::Local module in the standard distribution) to reduce structured
-dates to epoch seconds.  However, if you don't know the precise format of
-your dates, then you should probably use either of the Date::Manip and
-Date::Calc modules from CPAN before you go hacking up your own parsing
-routine to handle arbitrary date formats.
+(contributed by brian d foy)
+
+You could just store all your dates as a number and then subtract. Life
+isn't always that simple though. If you want to work with formatted
+dates, the Date::Manip, Date::Calc, or DateTime modules can help you.
+
 
 =head2 How can I take a string and turn it into epoch seconds?
 
@@ -532,8 +529,8 @@ C<$timestamp = gmtime(1005613200)> sets $timestamp to "Tue Nov 13 01:00:00
 
 That doesn't mean that Perl can't be used to create non-Y2K compliant
 programs.  It can.  But so can your pencil.  It's the fault of the user,
-not the language.  At the risk of inflaming the NRA: ``Perl doesn't
-break Y2K, people do.''  See http://www.perl.org/about/y2k.html for
+not the language.  At the risk of inflaming the NRA: "Perl doesn't
+break Y2K, people do."  See http://www.perl.org/about/y2k.html for
 a longer exposition.
 
 =head1 Data: Strings
@@ -553,7 +550,7 @@ and C<Data::Validate::IP>.
 
 =head2 How do I unescape a string?
 
-It depends just what you mean by ``escape''.  URL escapes are dealt
+It depends just what you mean by "escape".  URL escapes are dealt
 with in L<perlfaq9>.  Shell escapes with the backslash (C<\>)
 character are removed with
 
@@ -593,7 +590,7 @@ does not show up next to itself
 This is documented in L<perlref>, and although it's not the easiest
 thing to read, it does work. In each of these examples, we call the
 function inside the braces of used to dereference a reference. If we
-have a more than one return value, we can contruct and dereference an
+have a more than one return value, we can construct and dereference an
 anonymous array. In this case, we call the function in list context.
 
     print "The time values are @{ [localtime] }.\n";
@@ -1215,6 +1212,8 @@ same thing.
 
 =head2 How can I tell whether a certain element is contained in a list or array?
 
+(portions of this answer contributed by Anno Siegel)
+
 Hearing the word "in" is an I<in>dication that you probably should have
 used a hash, not a list or array, to store your data.  Hashes are
 designed to answer this question quickly and efficiently.  Arrays aren't.
@@ -1250,28 +1249,35 @@ quite a lot of space by using bit strings instead:
 
 Now check whether C<vec($read,$n,1)> is true for some C<$n>.
 
-Please do not use
+These methods guarantee fast individual tests but require a re-organization
+of the original list or array.  They only pay off if you have to test
+multiple values against the same array.
 
-    ($is_there) = grep $_ eq $whatever, @array;
+If you are testing only once, the standard module List::Util exports
+the function C<first> for this purpose.  It works by stopping once it
+finds the element. It's written in C for speed, and its Perl equivalant
+looks like this subroutine:
 
-or worse yet
+       sub first (&@) {
+               my $code = shift;
+               foreach (@_) {
+                       return $_ if &{$code}();
+               }
+               undef;
+       }
 
-    ($is_there) = grep /$whatever/, @array;
+If speed is of little concern, the common idiom uses grep in scalar context
+(which returns the number of items that passed its condition) to traverse the
+entire list. This does have the benefit of telling you how many matches it
+found, though.
 
-These are slow (checks every element even if the first matches),
-inefficient (same reason), and potentially buggy (what if there are
-regex characters in $whatever?).  If you're only testing once, then
-use:
+       my $is_there = grep $_ eq $whatever, @array;
 
-    $is_there = 0;
-    foreach $elt (@array) {
-       if ($elt eq $elt_to_find) {
-           $is_there = 1;
-           last;
-       }
-    }
-    if ($is_there) { ... }
+If you want to actually extract the matching elements, simply use grep in
+list context.
 
+       my @matches = grep $_ eq $whatever, @array;
+       
 =head2 How do I compute the difference of two arrays?  How do I compute the intersection of two arrays?
 
 Use a hash.  Here's code to do both and more.  It assumes that
@@ -1780,27 +1786,50 @@ such as each().
 
 =head2 How do I sort a hash (optionally by value instead of key)?
 
-Internally, hashes are stored in a way that prevents you from imposing
-an order on key-value pairs.  Instead, you have to sort a list of the
-keys or values:
+(contributed by brian d foy)
+
+To sort a hash, start with the keys. In this example, we give the list of
+keys to the sort function which then compares them ASCIIbetically (which
+might be affected by your locale settings). The output list has the keys
+in ASCIIbetical order. Once we have the keys, we can go through them to
+create a report which lists the keys in ASCIIbetical order.
 
-    @keys = sort keys %hash;   # sorted by key
-    @keys = sort {
-                   $hash{$a} cmp $hash{$b}
-           } keys %hash;       # and by value
+       my @keys = sort { $a cmp $b } keys %hash;
+       
+       foreach my $key ( @keys )
+               {
+               printf "%-20s %6d\n", $key, $hash{$value};
+               }
+
+We could get more fancy in the C<sort()> block though. Instead of 
+comparing the keys, we can compute a value with them and use that
+value as the comparison.  
+
+For instance, to make our report order case-insensitive, we use
+the C<\L> sequence in a double-quoted string to make everything 
+lowercase. The C<sort()> block then compares the lowercased
+values to determine in which order to put the keys.
+
+       my @keys = sort { "\L$a" cmp "\L$b" } keys %hash;
+       
+Note: if the computation is expensive or the hash has many elements,
+you may want to look at the Schwartzian Transform to cache the 
+computation results.
+
+If we want to sort by the hash value instead, we use the hash key
+to look it up. We still get out a list of keys, but this time they
+are ordered by their value.
+
+       my @keys = sort { $hash{$a} <=> $hash{$b} } keys %hash;
 
-Here we'll do a reverse numeric sort by value, and if two keys are
-identical, sort by length of key, or if that fails, by straight ASCII
-comparison of the keys (well, possibly modified by your locale--see
-L<perllocale>).
+From there we can get more complex. If the hash values are the same,
+we can provide a secondary sort on the hash key.
 
-    @keys = sort {
-               $hash{$b} <=> $hash{$a}
-                         ||
-               length($b) <=> length($a)
-                         ||
-                     $a cmp $b
-    } keys %hash;
+       my @keys = sort { 
+               $hash{$a} <=> $hash{$b} 
+                       or
+               "\L$a" cmp "\L$b"
+               } keys %hash;
 
 =head2 How can I always keep my hash sorted?
 
@@ -1985,8 +2014,18 @@ in L<perltoot>.
 
 =head2 How can I use a reference as a hash key?
 
-You can't do this directly, but you could use the standard Tie::RefHash
-module distributed with Perl.
+(contributed by brian d foy)
+
+Hash keys are strings, so you can't really use a reference as the key.
+When you try to do that, perl turns the reference into its stringified
+form (for instance, C<HASH(0xDEADBEEF)>). From there you can't get back
+the reference from the stringified form, at least without doing some
+extra work on your own. Also remember that hash keys must be unique, but
+two different variables can store the same reference (and those variables
+can change later).
+
+The Tie::RefHash module, which is distributed with perl, might be what
+you want. It handles that extra work.
 
 =head1 Data: Misc
 
@@ -2037,7 +2076,7 @@ function.  Its semantics are somewhat cumbersome, so here's a C<getnum>
 wrapper function for more convenient access.  This function takes
 a string and returns the number it found, or C<undef> for input that
 isn't a C float.  The C<is_numeric> function is a front end to C<getnum>
-if you just want to say, ``Is this a float?''
+if you just want to say, "Is this a float?"
 
     sub getnum {
         use POSIX qw(strtod);