X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=pod%2Fperlfaq4.pod;h=815a9ea428ceb999d4b9981f21773ced86bf8042;hb=28b41a8090d259cff9b1dd87c0c53b3c4a31e822;hp=818225bc67bb1a1d56045d7c57217358a33d8736;hpb=5cd0b561c2b8d21210f0eb90d689d190c3da3c72;p=p5sagit%2Fp5-mst-13.2.git diff --git a/pod/perlfaq4.pod b/pod/perlfaq4.pod index 818225b..815a9ea 100644 --- a/pod/perlfaq4.pod +++ b/pod/perlfaq4.pod @@ -1,6 +1,6 @@ =head1 NAME -perlfaq4 - Data Manipulation ($Revision: 1.43 $, $Date: 2003/02/23 20:25:09 $) +perlfaq4 - Data Manipulation ($Revision: 1.56 $, $Date: 2004/11/03 22:47:56 $) =head1 DESCRIPTION @@ -28,6 +28,24 @@ L<"Floating Point Arithmetic"|perlop> for more details. my $number = sprintf "%.2f", 10/3; +=head2 Why is int() broken? + +Your int() is most probably working just fine. It's the numbers that +aren't quite what you think. + +First, see the above item "Why am I getting long decimals +(eg, 19.9499999999999) instead of the numbers I should be getting +(eg, 19.95)?". + +For example, this + + print int(0.6/0.2-2), "\n"; + +will in most computers print 0, not 1, because even such simple +numbers as 0.6 and 0.2 cannot be presented exactly by floating-point +numbers. What you think in the above as 'three' is really more like +2.9999999999999995559. + =head2 Why isn't my octal data interpreted correctly? Perl only understands octal and hex numbers as such when they occur as @@ -102,7 +120,7 @@ Perl numbers whose absolute values are integers under 2**31 (on 32 bit machines) will work pretty much like mathematical integers. Other numbers are not guaranteed. -=head2 How do I convert between numeric representations? +=head2 How do I convert between numeric representations/bases/radixes? As always with Perl there is more than one way to do it. Below are a few examples of approaches to making common conversions @@ -121,18 +139,15 @@ programmers the notation might be familiar. Using perl's built in conversion of 0x notation: - $int = 0xDEADBEEF; - $dec = sprintf("%d", $int); + $dec = 0xDEADBEEF; Using the hex function: - $int = hex("DEADBEEF"); - $dec = sprintf("%d", $int); + $dec = hex("DEADBEEF"); Using pack: - $int = unpack("N", pack("H8", substr("0" x 8 . "DEADBEEF", -8))); - $dec = sprintf("%d", $int); + $dec = unpack("N", pack("H8", substr("0" x 8 . "DEADBEEF", -8))); Using the CPAN module Bit::Vector: @@ -144,13 +159,14 @@ Using the CPAN module Bit::Vector: Using sprintf: - $hex = sprintf("%X", 3735928559); + $hex = sprintf("%X", 3735928559); # upper case A-F + $hex = sprintf("%x", 3735928559); # lower case a-f -Using unpack +Using unpack: $hex = unpack("H*", pack("N", 3735928559)); -Using Bit::Vector +Using Bit::Vector: use Bit::Vector; $vec = Bit::Vector->new_Dec(32, -559038737); @@ -167,13 +183,11 @@ And Bit::Vector supports odd bit counts: Using Perl's built in conversion of numbers with leading zeros: - $int = 033653337357; # note the leading 0! - $dec = sprintf("%d", $int); + $dec = 033653337357; # note the leading 0! Using the oct function: - $int = oct("33653337357"); - $dec = sprintf("%d", $int); + $dec = oct("33653337357"); Using Bit::Vector: @@ -188,7 +202,7 @@ Using sprintf: $oct = sprintf("%o", 3735928559); -Using Bit::Vector +Using Bit::Vector: use Bit::Vector; $vec = Bit::Vector->new_Dec(32, -559038737); @@ -199,13 +213,18 @@ Using Bit::Vector Perl 5.6 lets you write binary numbers directly with the 0b notation: - $number = 0b10110110; + $number = 0b10110110; -Using pack and ord +Using oct: + + my $input = "10110110"; + $decimal = oct( "0b$input" ); + +Using pack and ord: $decimal = ord(pack('B8', '10110110')); -Using pack and unpack for larger strings +Using pack and unpack for larger strings: $int = unpack("N", pack("B32", substr("0" x 32 . "11110101011011011111011101111", -32))); @@ -220,7 +239,11 @@ Using Bit::Vector: =item How do I convert from decimal to binary -Using unpack; +Using sprintf (perl 5.6+): + + $bin = sprintf("%b", 3735928559); + +Using unpack: $bin = unpack("B*", pack("N", 3735928559)); @@ -339,9 +362,20 @@ pseudorandom generator than comes with your operating system, look at =head2 How do I get a random number between X and Y? -Use the following simple function. It selects a random integer between -(and possibly including!) the two given integers, e.g., -C +C returns a number such that +C<< 0 <= rand($x) < $x >>. Thus what you want to have perl +figure out is a random number in the range from 0 to the +difference between your I and I. + +That is, to get a number between 10 and 15, inclusive, you +want a random number between 0 and 5 that you can then add +to 10. + + my $number = 10 + int rand( 15-10+1 ); + +Hence you derive the following simple function to abstract +that. It selects a random integer between the two given +integers (inclusive), For example: C. sub random_int_in ($$) { my($min, $max) = @_; @@ -359,7 +393,7 @@ The localtime function returns the day of the week. Without an argument localtime uses the current time. $day_of_year = (localtime)[7]; - + The POSIX module can also format a date as the day of the year or week of the year. @@ -369,7 +403,7 @@ week of the year. To get the day of year for any date, use the Time::Local module to get a time in epoch seconds for the argument to localtime. - + use POSIX qw/strftime/; use Time::Local; my $week_of_year = strftime "%W", @@ -380,7 +414,7 @@ The Date::Calc module provides two functions for to calculate these. use Date::Calc; my $day_of_year = Day_of_Year( 1987, 12, 18 ); my $week_of_year = Week_of_Year( 1987, 12, 18 ); - + =head2 How do I find the current century or millennium? Use the following simple functions: @@ -392,14 +426,6 @@ Use the following simple functions: return 1+int((((localtime(shift || time))[5] + 1899))/1000); } -You can also use the POSIX strftime() function which may be a bit -slower but is easier to read and maintain. - - use POSIX qw/strftime/; - - my $week_of_the_year = strftime "%W", localtime; - my $day_of_the_year = strftime "%j", localtime; - On some systems, the POSIX module's strftime() function has been extended in a non-standard way to use a C<%C> format, which they sometimes claim is the "century". It isn't, @@ -533,7 +559,7 @@ 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://language.perl.com/news/y2k.html for +break Y2K, people do.'' See http://www.perl.org/about/y2k.html for a longer exposition. =head1 Data: Strings @@ -762,6 +788,19 @@ case", but that's not quite accurate. Consider the proper capitalization of the movie I, for example. +Damian Conway's L module provides some smart +case transformations: + + use Text::Autoformat; + my $x = "Dr. Strangelove or: How I Learned to Stop ". + "Worrying and Love the Bomb"; + + print $x, "\n"; + for my $style (qw( sentence title highlight )) + { + print autoformat($x, { case => $style }), "\n"; + } + =head2 How can I split a [character] delimited string except when inside [character]? Several modules can handle this sort of pasing---Text::Balanced, @@ -1453,16 +1492,11 @@ the hash is to be modified. Use the rand() function (see L): - # at the top of the program: - srand; # not needed for 5.004 and later - - # then later on $index = rand @array; $element = $array[$index]; -Make sure you I. -If you are calling it more than once (such as before each -call to rand), you're almost certainly doing something wrong. +Or, simply: + my $element = $array[ rand @array ]; =head2 How do I permute N elements of a list? @@ -1550,7 +1584,7 @@ If you need to sort on several fields, the following paradigm is useful. This can be conveniently combined with precalculation of keys as given above. -See the F artitcle article in the "Far More Than You Ever Wanted +See the F article in the "Far More Than You Ever Wanted To Know" collection in http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz for more about this approach. @@ -1681,19 +1715,15 @@ sorting the keys as shown in an earlier question. =head2 What happens if I add or remove keys from a hash while iterating over it? -Don't do that. :-) +(contributed by brian d foy) -[lwall] In Perl 4, you were not allowed to modify a hash at all while -iterating over it. In Perl 5 you can delete from it, but you still -can't add to it, because that might cause a doubling of the hash table, -in which half the entries get copied up to the new top half of the -table, at which point you've totally bamboozled the iterator code. -Even if the table doesn't double, there's no telling whether your new -entry will be inserted before or after the current iterator position. +The easy answer is "Don't do that!" -Either treasure up your changes and make them after the iterator finishes -or use keys to fetch all the old keys at once, and iterate over the list -of keys. +If you iterate through the hash with each(), you can delete the key +most recently returned without worrying about it. If you delete or add +other keys, the iterator may skip or double up on them since perl +may rearrange the hash table. See the +entry for C in L. =head2 How do I look up a hash element by value? @@ -1972,13 +2002,17 @@ Assuming that you don't care about IEEE notations like "NaN" or if (/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/) { print "a C float\n" } -You can also use the L module on -the CPAN, which exports functions that validate data types -using these and other regular expressions, or you can use -the C module from CPAN which has regular -expressions to match various types of numbers. - -If you're on a POSIX system, Perl's supports the C +There are also some commonly used modules for the task. +L (distributed with 5.8) provides access to perl's +internal function C for determining +whether a variable looks like a number. L +exports functions that validate data types using both the +above and other regular expressions. Thirdly, there is +C which has regular expressions to match +various types of numbers. Those three modules are available +from the CPAN. + +If you're on a POSIX system, Perl supports the C function. Its semantics are somewhat cumbersome, so here's a C wrapper function for more convenient access. This function takes a string and returns the number it found, or C for input that @@ -2001,7 +2035,7 @@ if you just want to say, ``Is this a float?'' sub is_numeric { defined getnum($_[0]) } -Or you could check out the L module on the CPAN +Or you could check out the L module on the CPAN instead. The POSIX module (part of the standard Perl distribution) provides the C and C for converting strings to double and longs, respectively. @@ -2024,8 +2058,9 @@ and C functions: =head2 How do I print out or copy a recursive data structure? The Data::Dumper module on CPAN (or the 5.005 release of Perl) is great -for printing out data structures. The Storable module, found on CPAN, -provides a function called C that recursively copies its argument. +for printing out data structures. The Storable module on CPAN (or the +5.8 release of Perl), provides a function called C that recursively +copies its argument. use Storable qw(dclone); $r2 = dclone($r1);