X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=pod%2Fperlfaq4.pod;h=22ea1ae5814fb300ba5b04db36d4c2761341caf6;hb=58103a2e295c15d87c7ce0bd8dd83d7e110adac4;hp=5f305681c26ca367cf507d94b01aa5be982f69ed;hpb=b432a67249666bce4aa3385263660dc667d150d7;p=p5sagit%2Fp5-mst-13.2.git diff --git a/pod/perlfaq4.pod b/pod/perlfaq4.pod index 5f30568..22ea1ae 100644 --- a/pod/perlfaq4.pod +++ b/pod/perlfaq4.pod @@ -1,6 +1,6 @@ =head1 NAME -perlfaq4 - Data Manipulation ($Revision: 1.64 $, $Date: 2005/04/27 00:18:04 $) +perlfaq4 - Data Manipulation ($Revision: 1.73 $, $Date: 2005/12/31 00:54:37 $) =head1 DESCRIPTION @@ -392,7 +392,7 @@ integers (inclusive), For example: C. 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? @@ -487,20 +484,20 @@ Use one of the Date modules. The C module makes it simple, and give you the same time of day, only the day before. use DateTime; - + my $yesterday = DateTime->now->subtract( days => 1 ); - + print "Yesterday was $yesterday\n"; You can also use the C module using its Today_and_Now function. use Date::Calc qw( Today_and_Now Add_Delta_DHMS ); - + my @date_time = Add_Delta_DHMS( Today_and_Now(), -1, 0, 0, 0 ); - + print "@date\n"; - + Most people try to use the time rather than the calendar to figure out dates, but that assumes that days are twenty-four hours each. For most people, there are two days a year when they aren't: the switch to @@ -592,11 +589,11 @@ does not show up next to itself This is documented in L, 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 +function inside the braces used to dereference a reference. If we +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"; + print "The time values are @{ [localtime] }.\n"; If we want to call the function in scalar context, we have to do a bit more work. We can really have any code we like inside the braces, so @@ -604,18 +601,26 @@ we simply have to end with the scalar reference, although how you do that is up to you, and you can use code inside the braces. print "The time is ${\(scalar localtime)}.\n" - + print "The time is ${ my $x = localtime; \$x }.\n"; - + If your function already returns a reference, you don't need to create the reference yourself. sub timestamp { my $t = localtime; \$t } - + print "The time is ${ timestamp() }.\n"; - -In most cases, it is probably easier to simply use string -concatenation, which also forces scalar context. + +The C module can also do a lot of magic for you. You can +specify a variable name, in this case C, to set up a tied hash that +does the interpolation for you. It has several other methods to do this +as well. + + use Interpolation E => 'eval'; + print "The time values are $E{localtime()}.\n"; + +In most cases, it is probably easier to simply use string concatenation, +which also forces scalar context. print "The time is " . localtime . ".\n"; @@ -1215,6 +1220,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 Idication 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,27 +1257,34 @@ quite a lot of space by using bit strings instead: Now check whether C 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 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? @@ -1780,27 +1794,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. + + my @keys = sort { $a cmp $b } keys %hash; + + foreach my $key ( @keys ) + { + printf "%-20s %6d\n", $key, $hash{$value}; + } - @keys = sort keys %hash; # sorted by key - @keys = sort { - $hash{$a} cmp $hash{$b} - } keys %hash; # and by value +We could get more fancy in the C block though. Instead of +comparing the keys, we can compute a value with them and use that +value as the comparison. -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). +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 block then compares the lowercased +values to determine in which order to put the keys. - @keys = sort { - $hash{$b} <=> $hash{$a} - || - length($b) <=> length($a) - || - $a cmp $b - } keys %hash; + 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; + +From there we can get more complex. If the hash values are the same, +we can provide a secondary sort on the hash key. + + 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 +2022,18 @@ in L. =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). 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 @@ -2108,7 +2155,7 @@ the PDL module from CPAN instead--it makes number-crunching easy. =head1 AUTHOR AND COPYRIGHT -Copyright (c) 1997-2005 Tom Christiansen, Nathan Torkington, and +Copyright (c) 1997-2006 Tom Christiansen, Nathan Torkington, and other authors as noted. All rights reserved. This documentation is free; you can redistribute it and/or modify it