Thursday, May 22, 2014

why i stopped using perl

I wanted to have a double loop over key-value pairs in a hash. I was doing a sensitivity analysis, so I had a hash that linked parameter names to their values. I wanted to loop over the values in that hash, modifying the values one by one. In each loop, I would copy the original hash, change one value, then run the simulation.

So if I had parameters a, b, and c, each with initial value 1.0, I would want to run simulations where a was changed but b and c left alone, then b changed but a and c left alone, and then c changed.

If you do this the naive way in python, you would get screwed:

d = {'a': 1, 'b': 1, 'c': 1}
for key, value in d.items():
    new_d = d
    new_d[key] = value*1.01
    print new_d


{'a': 1.01, 'c': 1, 'b': 1}
{'a': 1.01, 'c': 1.01, 'b': 1}
{'a': 1.01, 'c': 1.01, 'b': 1.01}

because new_d is never a new object, it just points to the original dictionary d. I verified this by printing id(d) and id(new_d):


Changing that line to new_d = dict(d) suitably rescues the result:

{'a': 1.01, 'c': 1, 'b': 1}
{'a': 1, 'c': 1.01, 'b': 1}
{'a': 1, 'c': 1, 'b': 1.01}

Doing the (I thought) sensible thing in perl leads to bizarre results:

use 5.10.1;
my %hash = ("a" => 1, "b" => 1, "c" => 1);

while (my ($key, $value) = each %hash) {
    my %new_hash = %hash;
    $new_hash{$key} = value*1.01;
    print "$_: $new_hash{$_}, " for (keys %new_hash);
    print "\n";

causes in infinite loop:

c: 1.01, a: 1, b: 1, 
c: 1.01, a: 1, b: 1, 
c: 1.01, a: 1, b: 1, 
c: 1.01, a: 1, b: 1, 

I thought I had made the python mistake, so I printed \%hash and \%hash_new, but I saw different memory addresses:


It turns out that in perl, every hash has exactly one internal iterator. In the first step of the while-loop, that iterator points to c. When I copy the hash, the iterator goes back to the beginning and ends up pointing to c again in the next iteration of the loop.

You can rescue this in a bunch of ways, one of which is to just loop over keys:

for my $key (keys %hash) {

but I was so disgusted by this bizarre and crazy behavior that I just stopped using perl.

No comments:

Post a Comment