⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 perlfaq4.pod

📁 ARM上的如果你对底层感兴趣
💻 POD
📖 第 1 页 / 共 3 页
字号:
Alternatively, the Text::ParseWords module (part of the standard perl
distribution) lets you say:

    use Text::ParseWords;
    @new = quotewords(",", 0, $text);

=head2 How do I strip blank space from the beginning/end of a string?

Although the simplest approach would seem to be:

    $string =~ s/^\s*(.*?)\s*$/$1/;

This is unneccesarily slow, destructive, and fails with embedded newlines.
It is much better faster to do this in two steps:

    $string =~ s/^\s+//;
    $string =~ s/\s+$//;

Or more nicely written as:

    for ($string) {
	s/^\s+//;
	s/\s+$//;
    }

This idiom takes advantage of the C<foreach> loop's aliasing
behavior to factor out common code.  You can do this
on several strings at once, or arrays, or even the 
values of a hash if you use a slide:

    # trim whitespace in the scalar, the array, 
    # and all the values in the hash
    foreach ($scalar, @array, @hash{keys %hash}) {
        s/^\s+//;
        s/\s+$//;
    }

=head2 How do I extract selected columns from a string?

Use substr() or unpack(), both documented in L<perlfunc>.
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 { 
	my(@positions) = @_;
	my $template  = '';
	my $lastpos   = 1;
	for my $place (@positions) {
	    $template .= "A" . ($place - $lastpos) . " "; 
	    $lastpos   = $place;
	}
	$template .= "A*";
	return $template;
    }

=head2 How do I find the soundex value of a string?

Use the standard Text::Soundex module distributed with perl.

=head2 How can I expand variables in text strings?

Let's assume that you have a string like:

    $text = 'this has a $foo in it and a $bar';

If those were both global variables, then this would
suffice:

    $text =~ s/\$(\w+)/${$1}/g;

But since they are probably lexicals, or at least, they could
be, you'd have to do this:

    $text =~ s/(\$\w+)/$1/eeg;
    die if $@;			# needed on /ee, not /e

It's probably better in the general case to treat those
variables as entries in some special hash.  For example:

    %user_defs = ( 
	foo  => 23,
	bar  => 19,
    );
    $text =~ s/\$(\w+)/$user_defs{$1}/g;

See also ``How do I expand function calls in a string?'' in this section
of the FAQ.

=head2 What's wrong with always quoting "$vars"?

The problem is that those double-quotes force stringification,
coercing numbers and references into strings, even when you
don't want them to be.

If you get used to writing odd things like these:

    print "$var";   	# BAD
    $new = "$old";   	# BAD
    somefunc("$var");	# BAD

You'll be in trouble.  Those should (in 99.8% of the cases) be
the simpler and more direct:

    print $var;
    $new = $old;
    somefunc($var);

Otherwise, besides slowing you down, you're going to break code when
the thing in the scalar is actually neither a string nor a number, but
a reference:

    func(\@array);
    sub func {
	my $aref = shift;
	my $oref = "$aref";  # WRONG
    }

You can also get into subtle problems on those few operations in Perl
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.  

    @lines = `command`;
    print "@lines";		# WRONG - extra blanks
    print @lines;		# right

=head2 Why don't my <<HERE documents work?

Check for these three things:

=over 4

=item 1. There must be no space after the << part.

=item 2. There (probably) should be a semicolon at the end.

=item 3. 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 
can do this:

    # all in one
    ($VAR = <<HERE_TARGET) =~ s/^\s+//gm;
        your text
        goes here
    HERE_TARGET

But the HERE_TARGET must still be flush against the margin.
If you want that indented also, you'll have to quote 
in the indentation.

    ($quote = <<'    FINIS') =~ s/^\s+//gm;
            ...we will have peace, when you and all your works have
            perished--and the works of your dark master to whom you
            would deliver us. You are a liar, Saruman, and a corrupter
            of men's hearts.  --Theoden in /usr/src/perl/taint.c
        FINIS
    $quote =~ s/\s*--/\n--/;

A nice general-purpose fixer-upper function for indented here documents
follows.  It expects to be called with a here document as its argument.
It looks to see whether each line begins with a common substring, and
if so, strips that off.  Otherwise, it takes the amount of leading
white space found on the first line and removes that much off each
subsequent line.

    sub fix {
        local $_ = shift;
        my ($white, $leader);  # common white space and common leading string
        if (/^\s*(?:([^\w\s]+)(\s*).*\n)(?:\s*\1\2?.*\n)+$/) {
            ($white, $leader) = ($2, quotemeta($1));
        } else {
            ($white, $leader) = (/^(\s+)/, '');
        }
        s/^\s*?$leader(?:$white)?//gm;
        return $_;
    }

This works with leading special strings, dynamically determined:

    $remember_the_main = fix<<'    MAIN_INTERPRETER_LOOP';
	@@@ int
	@@@ runops() {
	@@@     SAVEI32(runlevel);
	@@@     runlevel++;
	@@@     while ( op = (*op->op_ppaddr)() ) ;
	@@@     TAINT_NOT;
	@@@     return 0;
	@@@ }
    MAIN_INTERPRETER_LOOP

Or with a fixed amount of leading white space, with remaining
indentation correctly preserved:

    $poem = fix<<EVER_ON_AND_ON;
       Now far ahead the Road has gone,
	  And I must follow, if I can,
       Pursuing it with eager feet,
	  Until it joins some larger way
       Where many paths and errands meet.
	  And whither then? I cannot say.
		--Bilbo in /usr/src/perl/pp_ctl.c
    EVER_ON_AND_ON

=head1 Data: Arrays

=head2 What is the difference between $array[1] and @array[1]?

The former is a scalar value, the latter an array slice, which makes
it a list with one (scalar) value.  You should use $ when you want a
scalar value (most of the time) and @ when you want a list with one
scalar value in it (very, very rarely; nearly never, in fact).

Sometimes it doesn't make a difference, but sometimes it does.
For example, compare:

    $good[0] = `some program that outputs several lines`;

with

    @bad[0]  = `same program that outputs several lines`;

The B<-w> flag will warn you about these matters.

=head2 How can I extract just the unique elements of an array?

There are several possible ways, depending on whether the array is
ordered and whether you wish to preserve the ordering.

=over 4

=item a) If @in is sorted, and you want @out to be sorted:
(this assumes all true values in the array)

    $prev = 'nonesuch';
    @out = grep($_ ne $prev && ($prev = $_), @in);

This is nice in that it doesn't use much extra memory, simulating
uniq(1)'s behavior of removing only adjacent duplicates.  It's less
nice in that it won't work with false values like undef, 0, or "";
"0 but true" is ok, though.

=item b) If you don't know whether @in is sorted:

    undef %saw;
    @out = grep(!$saw{$_}++, @in);

=item c) Like (b), but @in contains only small integers:

    @out = grep(!$saw[$_]++, @in);

=item d) A way to do (b) without any loops or greps:

    undef %saw;
    @saw{@in} = ();
    @out = sort keys %saw;  # remove sort if undesired

=item e) Like (d), but @in contains only small positive integers:

    undef @ary;
    @ary[@in] = @in;
    @out = @ary;

=back

=head2 How can I tell whether a list or array contains a certain element?

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.

That being said, there are several ways to approach this.  If you
are going to make this query many times over arbitrary string values,
the fastest way is probably to invert the original array and keep an
associative array lying about whose keys are the first array's values.

    @blues = qw/azure cerulean teal turquoise lapis-lazuli/;
    undef %is_blue;
    for (@blues) { $is_blue{$_} = 1 }

Now you can check whether $is_blue{$some_color}.  It might have been a
good idea to keep the blues all in a hash in the first place.

If the values are all small integers, you could use a simple indexed
array.  This kind of an array will take up less space:

    @primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31);
    undef @is_tiny_prime;
    for (@primes) { $is_tiny_prime[$_] = 1; }

Now you check whether $is_tiny_prime[$some_number].

If the values in question are integers instead of strings, you can save
quite a lot of space by using bit strings instead:

    @articles = ( 1..10, 150..2000, 2017 );
    undef $read;
    for (@articles) { vec($read,$_,1) = 1 }

Now check whether C<vec($read,$n,1)> is true for some C<$n>.

Please do not use

    $is_there = grep $_ eq $whatever, @array;

or worse yet

    $is_there = grep /$whatever/, @array;

These are slow (checks every element even if the first matches),
inefficient (same reason), and potentially buggy (what if there are
regexp characters in $whatever?).

=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
each element is unique in a given array:

    @union = @intersection = @difference = ();
    %count = ();
    foreach $element (@array1, @array2) { $count{$element}++ }
    foreach $element (keys %count) {
	push @union, $element;
	push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element;
    }

=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:

    for ($i=0; $i < @array; $i++) {
        if ($array[$i] eq "Waldo") {
	    $found_index = $i;
            last;
        }
    }

Now C<$found_index> has what you want.

=head2 How do I handle linked lists?

In general, you usually don't need a linked list in Perl, since with
regular arrays, you can push and pop or shift and unshift at either end,
or you can use splice to add and/or remove arbitrary number of elements at
arbitrary points.  Both pop and shift are both O(1) operations on perl's
dynamic arrays.  In the absence of shifts and pops, push in general
needs to reallocate on the order every log(N) times, and unshift will
need to copy pointers each time.

If you really, really wanted, you could use structures as described in
L<perldsc> or L<perltoot> and do just what the algorithm book tells you
to do.

=head2 How do I handle circular lists?

Circular lists could be handled in the traditional fashion with linked
lists, or you could just do something like this with an array:

    unshift(@array, pop(@array));  # the last shall be first
    push(@array, shift(@array));   # and vice versa

=head2 How do I shuffle an array randomly?

Use this:

    # fisher_yates_shuffle( \@array ) : 
    # generate a random permutation of @array in place
    sub fisher_yates_shuffle {
        my $array = shift;
        my $i;
        for ($i = @$array; --$i; ) {
            my $j = int rand ($i+1);
            next if $i == $j;
            @$array[$i,$j] = @$array[$j,$i];
        }
    }

    fisher_yates_shuffle( \@array );    # permutes @array in place

You've probably seen shuffling algorithms that works using splice,
randomly picking another element to swap the current element with:

    srand;
    @new = ();
    @old = 1 .. 10;  # just a demo
    while (@old) {
	push(@new, splice(@old, rand @old, 1));
    }

This is bad because splice is already O(N), and since you do it N times,
you just invented a quadratic algorithm; that is, O(N**2).  This does
not scale, although Perl is so efficient that you probably won't notice
this until you have rather largish arrays.

=head2 How do I process/modify each element of an array?

Use C<for>/C<foreach>:

    for (@lines) {
	s/foo/bar/;	# change that word
	y/XZ/ZX/;	# swap those letters
    }

Here's another; let's compute spherical volumes:

    for (@volumes = @radii) {   # @volumes has changed parts
	$_ **= 3;
	$_ *= (4/3) * 3.14159;  # this will be constant folded
    }

If you want to do the same thing to modify the values of the hash,
you may not use the C<values> function, oddly enough.  You need a slice:

    for $orbit ( @orbits{keys %orbits} ) {
	($orbit **= 3) *= (4/3) * 3.14159; 
    }

=head2 How do I select a random element from an array?

Use the rand() function (see L<perlfunc/rand>):

    # 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<only call srand once per program, if then>.
If you are calling it more than once (such as before each 
call to rand), you're almost certainly doing something wrong.

=head2 How do I permute N elements of a list?

Here's a little program that generates all permutations
of all the words on each line of input.  The algorithm embodied
in the permute() function should work on any list:

    #!/usr/bin/perl -n
    # tsc-permute: permute each word of input
    permute([split], []);
    sub permute {
        my @items = @{ $_[0] };
        my @perms = @{ $_[1] };
        unless (@items) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -