简体   繁体   中英

Perl for loop assignment by reference

So I am wondering if there is a better way of doing the following:

I have an array of lines that I want to print out multiple times and replace certain things differently each time. For example:

my @array = ("I like most animals.\n", "Most animals like me.\n");
my @animals = ("dogs", "cats");
foreach my $animal (@animals) {
    foreach my $sentence (@array) {
        $sentence =~ s/animals/$animal/g;
        print $sentence;
    }
}

The problem with this being this only works the first time because animals get replaced in the original array. To get around this inside my for loop I just do something like my $line = $sentence; and then just regex and print $line instead. Is there a better way of doing this?

The r flag in newer perls can help (5.14 onwards)

The problem is - if you do a "sed style" pattern replace on $sentence it's an alias to the array element, not a variable in it's own right.

That's because of how array iterators work - it allows you to modify an element in a loop, and have it update the original:

foreach my $value ( @list_of_things ) { 
    $value++;
}

Will alter @list_of_things . And this is effectively what your pattern replacement is doing. We need to copy it before we do that, so we can preserve the original - or use the r flag - to avoid that happening.

So the easiest way is:

#!/usr/bin/perl

use warnings;
use strict;

my @array = ("I like most animals.\n", "Most animals like me.\n");
my @animals = ("dogs", "cats");
foreach my $animal (@animals) {
    foreach my $sentence (@array) {
        print $sentence=~ s/animals/$animal/r;
    }
}

You can probably lose the g as well, as it appears to be redundant.

Output:

I like most dogs.
Most dogs like me.
I like most cats.
Most cats like me.

But you don't actually need $sentence any more at all:

foreach my $animal (@animals) {
    for (@array) {
        print s/animals/$animal/r;
    }
}

In older perls, as choroba notes - you can do the same by copying the alias to a local variable:

(my $s = $sentence) =~ s/animals/$animal/g;
print $s;

What you are looking for is a template. Here is an example using Template :

#!/usr/bin/perl
use warnings;
use strict;

use Template;

my @array = ( "I like most [% animals %].\n",
              "Most [% animals %] like me.\n",
            );
my @animals = qw( dogs cats );

my $template = 'Template'->new;

for my $animal (@animals) {
    for my $sentence (@array) {
        $template->process(\$sentence, { animals => $animal });
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM