X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=pod%2Fperlfaq4.pod;h=818225bc67bb1a1d56045d7c57217358a33d8736;hb=3874323d73ef08e6639270ea07e834aec3e379f1;hp=b5305164318b2745c1c2037523445e7f6d3cdaf8;hpb=881bdbd4cff2623b5a5979fcc7b7c3078938e0fd;p=p5sagit%2Fp5-mst-13.2.git diff --git a/pod/perlfaq4.pod b/pod/perlfaq4.pod index b530516..818225b 100644 --- a/pod/perlfaq4.pod +++ b/pod/perlfaq4.pod @@ -1,6 +1,6 @@ =head1 NAME -perlfaq4 - Data Manipulation ($Revision: 1.19 $, $Date: 2002/03/11 22:15:19 $) +perlfaq4 - Data Manipulation ($Revision: 1.43 $, $Date: 2003/02/23 20:25:09 $) =head1 DESCRIPTION @@ -11,65 +11,45 @@ numbers, dates, strings, arrays, hashes, and miscellaneous data issues. =head2 Why am I getting long decimals (eg, 19.9499999999999) instead of the numbers I should be getting (eg, 19.95)? -The infinite set that a mathematician thinks of as the real numbers can -only be approximated on a computer, since the computer only has a finite -number of bits to store an infinite number of, um, numbers. - -Internally, your computer represents floating-point numbers in binary. -Floating-point numbers read in from a file or appearing as literals -in your program are converted from their decimal floating-point -representation (eg, 19.95) to an internal binary representation. - -However, 19.95 can't be precisely represented as a binary -floating-point number, just like 1/3 can't be exactly represented as a -decimal floating-point number. The computer's binary representation -of 19.95, therefore, isn't exactly 19.95. - -When a floating-point number gets printed, the binary floating-point -representation is converted back to decimal. These decimal numbers -are displayed in either the format you specify with printf(), or the -current output format for numbers. (See L if you use -print. C<$#> has a different default value in Perl5 than it did in -Perl4. Changing C<$#> yourself is deprecated.) - -This affects B computer languages that represent decimal -floating-point numbers in binary, not just Perl. Perl provides -arbitrary-precision decimal numbers with the Math::BigFloat module -(part of the standard Perl distribution), but mathematical operations -are consequently slower. - -If precision is important, such as when dealing with money, it's good -to work with integers and then divide at the last possible moment. -For example, work in pennies (1995) instead of dollars and cents -(19.95) and divide by 100 at the end. - -To get rid of the superfluous digits, just use a format (eg, -C) to get the required precision. -See L. +Internally, your computer represents floating-point numbers +in binary. Digital (as in powers of two) computers cannot +store all numbers exactly. Some real numbers lose precision +in the process. This is a problem with how computers store +numbers and affects all computer languages, not just Perl. + +L show the gory details of number +representations and conversions. + +To limit the number of decimal places in your numbers, you +can use the printf or sprintf function. See the +L<"Floating Point Arithmetic"|perlop> for more details. + + printf "%.2f", 10/3; + + my $number = sprintf "%.2f", 10/3; =head2 Why isn't my octal data interpreted correctly? -Perl only understands octal and hex numbers as such when they occur -as literals in your program. Octal literals in perl must start with -a leading "0" and hexadecimal literals must start with a leading "0x". -If they are read in from somewhere and assigned, no automatic -conversion takes place. You must explicitly use oct() or hex() if you -want the values converted to decimal. oct() interprets -both hex ("0x350") numbers and octal ones ("0350" or even without the -leading "0", like "377"), while hex() only converts hexadecimal ones, -with or without a leading "0x", like "0x255", "3A", "ff", or "deadbeef". +Perl only understands octal and hex numbers as such when they occur as +literals in your program. Octal literals in perl must start with a +leading "0" and hexadecimal literals must start with a leading "0x". +If they are read in from somewhere and assigned, no automatic +conversion takes place. You must explicitly use oct() or hex() if you +want the values converted to decimal. oct() interprets hex ("0x350"), +octal ("0350" or even without the leading "0", like "377") and binary +("0b1010") numbers, while hex() only converts hexadecimal ones, with +or without a leading "0x", like "0x255", "3A", "ff", or "deadbeef". The inverse mapping from decimal to octal can be done with either the -"%o" or "%O" sprintf() formats. To get from decimal to hex try either -the "%x" or the "%X" formats to sprintf(). +"%o" or "%O" sprintf() formats. This problem shows up most often when people try using chmod(), mkdir(), -umask(), or sysopen(), which by widespread tradition typically take +umask(), or sysopen(), which by widespread tradition typically take permissions in octal. chmod(644, $file); # WRONG chmod(0644, $file); # right -Note the mistake in the first line was specifying the decimal literal +Note the mistake in the first line was specifying the decimal literal 644, rather than the intended octal literal 0644. The problem can be seen with: @@ -77,7 +57,7 @@ be seen with: Surely you had not intended C - did you? If you want to use numeric literals as arguments to chmod() et al. then please -try to express them as octal constants, that is with a leading zero and +try to express them as octal constants, that is with a leading zero and with the following digits restricted to the set 0..7. =head2 Does Perl have a round() function? What about ceil() and floor()? Trig functions? @@ -114,7 +94,7 @@ alternation: for ($i = 0; $i < 1.01; $i += 0.05) { printf "%.1f ",$i} - 0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7 + 0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7 0.8 0.8 0.9 0.9 1.0 1.0 Don't blame Perl. It's the same as in C. IEEE says we have to do this. @@ -135,7 +115,9 @@ functions is that it works with numbers of ANY size, that it is optimized for speed on some operations, and for at least some programmers the notation might be familiar. -=item B +=over 4 + +=item How do I convert hexadecimal into decimal Using perl's built in conversion of 0x notation: @@ -158,9 +140,9 @@ Using the CPAN module Bit::Vector: $vec = Bit::Vector->new_Hex(32, "DEADBEEF"); $dec = $vec->to_Dec(); -=item B +=item How do I convert from decimal to hexadecimal -Using sprint: +Using sprintf: $hex = sprintf("%X", 3735928559); @@ -181,7 +163,7 @@ And Bit::Vector supports odd bit counts: $vec->Resize(32); # suppress leading 0 if unwanted $hex = $vec->to_Hex(); -=item B +=item How do I convert from octal to decimal Using Perl's built in conversion of numbers with leading zeros: @@ -200,7 +182,7 @@ Using Bit::Vector: $vec->Chunk_List_Store(3, split(//, reverse "33653337357")); $dec = $vec->to_Dec(); -=item B +=item How do I convert from decimal to octal Using sprintf: @@ -212,7 +194,12 @@ Using Bit::Vector $vec = Bit::Vector->new_Dec(32, -559038737); $oct = reverse join('', $vec->Chunk_List_Read(3)); -=item B +=item How do I convert from binary to decimal + +Perl 5.6 lets you write binary numbers directly with +the 0b notation: + + $number = 0b10110110; Using pack and ord @@ -231,7 +218,7 @@ Using Bit::Vector: $vec = Bit::Vector->new_Bin(32, "11011110101011011011111011101111"); $dec = $vec->to_Dec(); -=item B +=item How do I convert from decimal to binary Using unpack; @@ -246,6 +233,7 @@ Using Bit::Vector: The remaining transformations (e.g. hex -> oct, bin -> hex, etc.) are left as an exercise to the inclined reader. +=back =head2 Why doesn't & work the way I want it to? @@ -256,7 +244,7 @@ C<00110011>). The operators work with the binary form of a number (the number C<3> is treated as the bit pattern C<00000011>). So, saying C<11 & 3> performs the "and" operation on numbers (yielding -C<1>). Saying C<"11" & "3"> performs the "and" operation on strings +C<3>). Saying C<"11" & "3"> performs the "and" operation on strings (yielding C<"1">). Most problems with C<&> and C<|> arise because the programmer thinks @@ -327,14 +315,17 @@ Get the http://www.cpan.org/modules/by-module/Roman module. If you're using a version of Perl before 5.004, you must call C once at the start of your program to seed the random number generator. + + BEGIN { srand() if $] < 5.004 } + 5.004 and later automatically call C at the beginning. Don't -call C more than once--you make your numbers less random, rather +call C more than once---you make your numbers less random, rather than more. Computers are good at being predictable and bad at being random (despite appearances caused by bugs in your programs :-). see the -F artitcle in the "Far More Than You Ever Wanted To Know" -collection in http://www.cpan.org/olddoc/FMTEYEWTK.tgz , courtesy of +F 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 who attempts to generate random numbers by deterministic means is, of course, living in a state of sin.'' @@ -362,29 +353,59 @@ C =head1 Data: Dates -=head2 How do I find the week-of-the-year/day-of-the-year? +=head2 How do I find the day or week of the year? -The day of the year is in the array returned by localtime() (see -L): +The localtime function returns the day of the week. Without an +argument localtime uses the current time. - $day_of_year = (localtime(time()))[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. + use POSIX qw/strftime/; + my $day_of_year = strftime "%j", localtime; + my $week_of_year = strftime "%W", localtime; + +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", + localtime( timelocal( 0, 0, 0, 18, 11, 1987 ) ); + +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: - sub get_century { + sub get_century { return int((((localtime(shift || time))[5] + 1999))/100); - } - sub get_millennium { + } + sub get_millennium { 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, you'll find that 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, because on most such systems, -this is only the first two digits of the four-digit year, and thus cannot -be used to reliably determine the current century or millennium. +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, +because on most such systems, this is only the first two +digits of the four-digit year, and thus cannot be used to +reliably determine the current century or millennium. =head2 How can I compare two dates and find the difference? @@ -430,58 +451,60 @@ modules. (Thanks to David Cassell for most of this text.) =head2 How do I find yesterday's date? -The C function returns the current time in seconds since the -epoch. Take twenty-four hours off that: +If you only need to find the date (and not the same time), you +can use the Date::Calc module. - $yesterday = time() - ( 24 * 60 * 60 ); + use Date::Calc qw(Today Add_Delta_Days); -Then you can pass this to C and get the individual year, -month, day, hour, minute, seconds values. + my @date = Add_Delta_Days( Today(), -1 ); -Note very carefully that the code above assumes that your days are -twenty-four hours each. For most people, there are two days a year -when they aren't: the switch to and from summer time throws this off. -A solution to this issue is offered by Russ Allbery. + print "@date\n"; + +Most people try to use the time rather than the calendar to +figure out dates, but that assumes that your days are +twenty-four hours each. For most people, there are two days +a year when they aren't: the switch to and from summer time +throws this off. Russ Allbery offers this solution. sub yesterday { - my $now = defined $_[0] ? $_[0] : time; - my $then = $now - 60 * 60 * 24; - my $ndst = (localtime $now)[8] > 0; - my $tdst = (localtime $then)[8] > 0; - $then - ($tdst - $ndst) * 60 * 60; - } - # Should give you "this time yesterday" in seconds since epoch relative to - # the first argument or the current time if no argument is given and - # suitable for passing to localtime or whatever else you need to do with - # it. $ndst is whether we're currently in daylight savings time; $tdst is - # whether the point 24 hours ago was in daylight savings time. If $tdst - # and $ndst are the same, a boundary wasn't crossed, and the correction - # will subtract 0. If $tdst is 1 and $ndst is 0, subtract an hour more - # from yesterday's time since we gained an extra hour while going off - # daylight savings time. If $tdst is 0 and $ndst is 1, subtract a - # negative hour (add an hour) to yesterday's time since we lost an hour. - # - # All of this is because during those days when one switches off or onto - # DST, a "day" isn't 24 hours long; it's either 23 or 25. - # - # The explicit settings of $ndst and $tdst are necessary because localtime - # only says it returns the system tm struct, and the system tm struct at - # least on Solaris doesn't guarantee any particular positive value (like, - # say, 1) for isdst, just a positive value. And that value can - # potentially be negative, if DST information isn't available (this sub - # just treats those cases like no DST). - # - # Note that between 2am and 3am on the day after the time zone switches - # off daylight savings time, the exact hour of "yesterday" corresponding - # to the current hour is not clearly defined. Note also that if used - # between 2am and 3am the day after the change to daylight savings time, - # the result will be between 3am and 4am of the previous day; it's - # arguable whether this is correct. - # - # This sub does not attempt to deal with leap seconds (most things don't). - # - # Copyright relinquished 1999 by Russ Allbery - # This code is in the public domain + my $now = defined $_[0] ? $_[0] : time; + my $then = $now - 60 * 60 * 24; + my $ndst = (localtime $now)[8] > 0; + my $tdst = (localtime $then)[8] > 0; + $then - ($tdst - $ndst) * 60 * 60; + } + +Should give you "this time yesterday" in seconds since epoch relative to +the first argument or the current time if no argument is given and +suitable for passing to localtime or whatever else you need to do with +it. $ndst is whether we're currently in daylight savings time; $tdst is +whether the point 24 hours ago was in daylight savings time. If $tdst +and $ndst are the same, a boundary wasn't crossed, and the correction +will subtract 0. If $tdst is 1 and $ndst is 0, subtract an hour more +from yesterday's time since we gained an extra hour while going off +daylight savings time. If $tdst is 0 and $ndst is 1, subtract a +negative hour (add an hour) to yesterday's time since we lost an hour. + +All of this is because during those days when one switches off or onto +DST, a "day" isn't 24 hours long; it's either 23 or 25. + +The explicit settings of $ndst and $tdst are necessary because localtime +only says it returns the system tm struct, and the system tm struct at +least on Solaris doesn't guarantee any particular positive value (like, +say, 1) for isdst, just a positive value. And that value can +potentially be negative, if DST information isn't available (this sub +just treats those cases like no DST). + +Note that between 2am and 3am on the day after the time zone switches +off daylight savings time, the exact hour of "yesterday" corresponding +to the current hour is not clearly defined. Note also that if used +between 2am and 3am the day after the change to daylight savings time, +the result will be between 3am and 4am of the previous day; it's +arguable whether this is correct. + +This sub does not attempt to deal with leap seconds (most things don't). + + =head2 Does Perl have a Year 2000 problem? Is Perl Y2K compliant? @@ -549,14 +572,6 @@ a subroutine call (in list context) into a string: print "My sub returned @{[mysub(1,2,3)]} that time.\n"; -If you prefer scalar context, similar chicanery is also useful for -arbitrary expressions: - - print "That yields ${\($n + 5)} widgets\n"; - -Version 5.004 of Perl had a bug that gave list context to the -expression in C<${...}>, but this is fixed in version 5.005. - See also ``How can I expand variables in text strings?'' in this section of the FAQ. @@ -567,8 +582,9 @@ matter how complicated. To find something between two single characters, a pattern like C will get the intervening bits in $1. For multiple ones, then something more like C would be needed. But none of these deals with -nested patterns, nor can they. For that you'll have to write a -parser. +nested patterns. For balanced expressions using C<(>, C<{>, C<[> +or C<< < >> as delimiters, use the CPAN module Regexp::Common, or see +L. For other cases, you'll have to write a parser. If you are serious about writing a parser, there are a number of modules or oddities that will make your life a lot easier. There are @@ -581,7 +597,7 @@ pull out the smallest nesting parts one at a time: while (s/BEGIN((?:(?!BEGIN)(?!END).)*)END//gs) { # do something with $1 - } + } A more complicated and sneaky approach is to make Perl's regular expression engine do it for you. This is courtesy Dean Inada, and @@ -636,22 +652,24 @@ done by making a shell alias, like so: See the documentation for Text::Autoformat to appreciate its many capabilities. -=head2 How can I access/change the first N letters of a string? +=head2 How can I access or change N characters of a string? -There are many ways. If you just want to grab a copy, use -substr(): +You can access the first characters of a string with substr(). +To get the first character, for example, start at position 0 +and grab the string of length 1. - $first_byte = substr($a, 0, 1); -If you want to modify part of a string, the simplest way is often to -use substr() as an lvalue: + $string = "Just another Perl Hacker"; + $first_char = substr( $string, 0, 1 ); # 'J' - substr($a, 0, 3) = "Tom"; +To change part of a string, you can use the optional fourth +argument which is the replacement string. -Although those with a pattern matching kind of thought process will -likely prefer + substr( $string, 13, 4, "Perl 5.8.0" ); - $a =~ s/^.../Tom/; +You can also use substr() as an lvalue. + + substr( $string, 13, 4 ) = "Perl 5.8.0"; =head2 How do I change the Nth occurrence of something? @@ -744,20 +762,21 @@ case", but that's not quite accurate. Consider the proper capitalization of the movie I, for example. -=head2 How can I split a [character] delimited string except when inside -[character]? (Comma-separated files) +=head2 How can I split a [character] delimited string except when inside [character]? + +Several modules can handle this sort of pasing---Text::Balanced, +Text::CVS, Text::CVS_XS, and Text::ParseWords, among others. -Take the example case of trying to split a string that is comma-separated -into its different fields. (We'll pretend you said comma-separated, not -comma-delimited, which is different and almost never what you mean.) You -can't use C because you shouldn't split if the comma is inside -quotes. For example, take a data line like this: +Take the example case of trying to split a string that is +comma-separated into its different fields. You can't use C +because you shouldn't split if the comma is inside quotes. For +example, take a data line like this: SAR001,"","Cimetrix, Inc","Bob Smith","CAM",N,8,1,0,7,"Error, Core Dumped" Due to the restriction of the quotes, this is a fairly complex -problem. Thankfully, we have Jeffrey Friedl, author of a highly -recommended book on regular expressions, to handle these for us. He +problem. Thankfully, we have Jeffrey Friedl, author of +I, to handle these for us. He suggests (assuming your string is contained in $text): @new = (); @@ -770,8 +789,7 @@ suggests (assuming your string is contained in $text): If you want to represent quotation marks inside a quotation-mark-delimited field, escape them with backslashes (eg, -C<"like \"this\"">. Unescaping them is a task addressed earlier in -this section. +C<"like \"this\"">. Alternatively, the Text::ParseWords module (part of the standard Perl distribution) lets you say: @@ -802,10 +820,10 @@ Or more nicely written as: This idiom takes advantage of the C loop's aliasing behavior to factor out common code. You can do this -on several strings at once, or arrays, or even the +on several strings at once, or arrays, or even the values of a hash if you use a slice: - # trim whitespace in the scalar, the array, + # trim whitespace in the scalar, the array, # and all the values in the hash foreach ($scalar, @array, @hash{keys %hash}) { s/^\s+//; @@ -814,9 +832,6 @@ values of a hash if you use a slice: =head2 How do I pad a string with blanks or pad a number with zeroes? -(This answer contributed by Uri Guttman, with kibitzing from -Bart Lateur.) - In the following examples, C<$pad_len> is the length to which you wish to pad the string, C<$text> or C<$num> contains the string to be padded, and C<$pad_char> contains the padding character. You can use a single @@ -831,13 +846,16 @@ right with blanks and it will truncate the result to a maximum length of C<$pad_len>. # Left padding a string with blanks (no truncation): - $padded = sprintf("%${pad_len}s", $text); + $padded = sprintf("%${pad_len}s", $text); + $padded = sprintf("%*s", $pad_len, $text); # same thing # Right padding a string with blanks (no truncation): - $padded = sprintf("%-${pad_len}s", $text); + $padded = sprintf("%-${pad_len}s", $text); + $padded = sprintf("%-*s", $pad_len, $text); # same thing - # Left padding a number with 0 (no truncation): - $padded = sprintf("%0${pad_len}d", $num); + # Left padding a number with 0 (no truncation): + $padded = sprintf("%0${pad_len}d", $num); + $padded = sprintf("%0*d", $pad_len, $num); # same thing # Right padding a string with blanks using pack (will truncate): $padded = pack("A$pad_len",$text); @@ -860,19 +878,19 @@ Left and right padding with any character, modifying C<$text> directly: =head2 How do I extract selected columns from a string? Use substr() or unpack(), both documented in L. -If you prefer thinking in terms of columns instead of widths, +If you prefer thinking in terms of columns instead of widths, you can use this kind of thing: # determine the unpack format needed to split Linux ps output # arguments are cut columns my $fmt = cut2fmt(8, 14, 20, 26, 30, 34, 41, 47, 59, 63, 67, 72); - sub cut2fmt { + sub cut2fmt { my(@positions) = @_; my $template = ''; my $lastpos = 1; for my $place (@positions) { - $template .= "A" . ($place - $lastpos) . " "; + $template .= "A" . ($place - $lastpos) . " "; $lastpos = $place; } $template .= "A*"; @@ -910,7 +928,7 @@ be, you'd have to do this: It's probably better in the general case to treat those variables as entries in some special hash. For example: - %user_defs = ( + %user_defs = ( foo => 23, bar => 19, ); @@ -924,7 +942,7 @@ of the FAQ. The problem is that those double-quotes force stringification-- coercing numbers and references into strings--even when you don't want them to be strings. Think of it this way: double-quote -expansion is used to produce new strings. If you already +expansion is used to produce new strings. If you already have a string, why do you need more? If you get used to writing odd things like these: @@ -955,27 +973,27 @@ that actually do care about the difference between a string and a number, such as the magical C<++> autoincrement operator or the syscall() function. -Stringification also destroys arrays. +Stringification also destroys arrays. @lines = `command`; print "@lines"; # WRONG - extra blanks print @lines; # right -=head2 Why don't my <EHERE documents work? Check for these three things: =over 4 -=item 1. There must be no space after the << part. +=item There must be no space after the EE part. -=item 2. There (probably) should be a semicolon at the end. +=item There (probably) should be a semicolon at the end. -=item 3. You can't (easily) have any space in front of the tag. +=item You can't (easily) have any space in front of the tag. =back -If you want to indent the text in the here document, you +If you want to indent the text in the here document, you can do this: # all in one @@ -985,7 +1003,7 @@ can do this: HERE_TARGET But the HERE_TARGET must still be flush against the margin. -If you want that indented also, you'll have to quote +If you want that indented also, you'll have to quote in the indentation. ($quote = <<' FINIS') =~ s/^\s+//gm; @@ -1080,7 +1098,7 @@ with @bad[0] = `same program that outputs several lines`; -The C pragma and the B<-w> flag will warn you about these +The C pragma and the B<-w> flag will warn you about these matters. =head2 How can I remove duplicate elements from a list or array? @@ -1236,8 +1254,8 @@ like this one. It uses the CPAN module FreezeThaw: @a = @b = ( "this", "that", [ "more", "stuff" ] ); printf "a and b contain %s arrays\n", - cmpStr(\@a, \@b) == 0 - ? "the same" + cmpStr(\@a, \@b) == 0 + ? "the same" : "different"; This approach also works for comparing hashes. Here @@ -1247,7 +1265,7 @@ we'll demonstrate two different answers: %a = %b = ( "this" => "that", "extra" => [ "more", "stuff" ] ); $a{EXTRA} = \%b; - $b{EXTRA} = \%a; + $b{EXTRA} = \%a; printf "a and b contain %s hashes\n", cmpStr(\%a, \%b) == 0 ? "the same" : "different"; @@ -1262,16 +1280,37 @@ an exercise to the reader. =head2 How do I find the first array element for which a condition is true? -You can use this if you care about the index: +To find the first array element which satisfies a condition, you can +use the first() function in the List::Util module, which comes with +Perl 5.8. This example finds the first element that contains "Perl". - for ($i= 0; $i < @array; $i++) { - if ($array[$i] eq "Waldo") { - $found_index = $i; - last; - } - } + use List::Util qw(first); + + my $element = first { /Perl/ } @array; -Now C<$found_index> has what you want. +If you cannot use List::Util, you can make your own loop to do the +same thing. Once you find the element, you stop the loop with last. + + my $found; + foreach my $element ( @array ) + { + if( /Perl/ ) { $found = $element; last } + } + +If you want the array index, you can iterate through the indices +and check the array element at each index until you find one +that satisfies the condition. + + my( $found, $index ) = ( undef, -1 ); + for( $i = 0; $i < @array; $i++ ) + { + if( $array[$i] =~ /Perl/ ) + { + $found = $array[$i]; + $index = $i; + last; + } + } =head2 How do I handle linked lists? @@ -1335,28 +1374,21 @@ lists, or you could just do something like this with an array: If you either have Perl 5.8.0 or later installed, or if you have Scalar-List-Utils 1.03 or later installed, you can say: - use List::Util 'shuffle'; + use List::Util 'shuffle'; @shuffled = shuffle(@list); -If not, you can use this: +If not, you can use a Fisher-Yates shuffle. - # fisher_yates_shuffle - # generate a random permutation of an array in place - # As in shuffling a deck of cards - # sub fisher_yates_shuffle { my $deck = shift; # $deck is a reference to an array my $i = @$deck; - while (--$i) { + while ($i--) { my $j = int rand ($i+1); @$deck[$i,$j] = @$deck[$j,$i]; } } -And here is an example of using it: - - # # shuffle my mpeg collection # my @mpeg =