perl help (foreach with %array)

My test code:

cat test.pl

$list{‘one’} = ‘two’;

foreach $name (%list)
{
print "Old: $name, New: $list{$name}
";
}

What happens:

perl test.pl

Old: one, New: two
Old: two, New:

My confusion: Why does foreach use ‘two’ for a second line of printout? There’s only one element in the array, isn’t there? I only want ‘one’ to map to ‘two’ but I’m getting a bogus mapping of ‘two’ to (blank).

I think when you access a hash like that, you get key-value pairs. What you want is something like:

foreach $key (keys %list)

etc

What you’ve got is a hash, not an array. In Perl, when you use a hash in a list context (such as the argument for a foreach loop) what you get back is a list of key/value pairs. So your loop is equivalent to



foreach $name ( 'one', 'two' ) { ... }


The first iteration looks up the key ‘one’ in the hash. The second iteration looks up the key ‘two’, which doesn’t exist, so you get blank output.

This is one of the many many many many many reasons why you should turn on ‘strict’ and ‘warnings’ whenever you’re writing Perl code. Here’s how to do it properly:



use strict;
use warnings;

my %list = ( one => 'two' );
# or you could say: my %list; $list{one} = 'two';

foreach my $name ( keys %list ) { 
    print "Old: $name, New: $list{$name}
";
}



Notice I’ve used the very handy ‘my’ keyword to scope variables to their enclosing block (or file in the case of %list). This is required under strict mode.

Ignorance fought. Using the keyword “keys” is the bit I was missing.

Thanks!

And friedo, thanks for the advice, but heck, I first learned programing back in '67 or so when “structure” was what a building had, not what you used in your code. We used GOTO like there was no tomorrow. I’m too old (now) to learn another way. :slight_smile:

I learned in 1968, but there is a good reason to use strict mode. Perl is non-declarative, and if you don’t and have a typo in a variable name you will get odd results. Using strict will cause errors which will let you catch this more easily.

You have to be aware of the structure because if you my something in a while loop, say, and then try to use that variable outside, you will get a totally different variable. You’ll also get an error if strict is turned on. So. not much to do with structure, more getting to the level of FORTRAN declarations.

Additional question (in case anyone is still watching this thread):

My variables actually have metacharacters in them, but I want them used as characters, not as wildcards:
$one=‘one+x’;
$two = ‘two’;
$list{$one} = $two;

$line = “one+x”;
$line =~ s/$one/$list{$one}/;
Is there any way to make this happen other than putting a backslash in front of any possible metacharacter before the substitution?

Thanks again!



$line =~ s/\Q$one\E/$list{$one}/;


\Q and \E are delimiters for telling the regex engine to ignore interpolated regex metachars. See the ever-useful perlre manpage.

friedo, you rawk. I did look for what I needed on that page but wan’t clever enough to find it.