Perl Lines of the Day from March, 1998

6 March, 1998

  @ADDRS = (@{+shift}, @EXTRA_ADDRS);

This isn't as complicated as previous examples, but I liked the way it looked. It appears in a site feedback program I wrote for a client. There is a subroutine, Send, that composes and sends an email message. The argument to the subroutine is a reference to a list of the recipients of the mail. There are certain additional recipients, listed in @EXTRA_ADDRS, who should receive copies of all feedback messages. (Debugging addresses, for example.)

The main point to note here is that the + sign on the shift is important. Without it, Perl would interpret @{shift} as a request for the contents of an array named shift. The + prevents shift from being taken as a variable name.

4 March, 1998

Today's line of the day:

  [map {[$first,@$_],['',@$_]} @$pow];

As promised, here's the explanation of the context of the 3 March entry. It appeared in a draft version of a power set function I wanted to write. Suppose you have a list L. The power set of such a list is a list of all the sublists that you can obtain from L by deletting elements from it. For example, the power set of (1, 2, 3) is the list of lists ((), (1), (2), (3), (1, 2), (1, 3), (2, 3), (1, 2, 3)), in some order.

My power set function went through several versions, one of which included the expression of 3 March. The way to construct the power set is: Delete the initial element i from your list, and (recursively) find the power set of the remaining items in the list. Make two copies of this result. Leave one of the copies alone; it's a list of all the sublists that don't contain i. Take the other copy and append i to each of its elements; that gives you a list of all the sublists that do contain i. Finally, append the two copies together; that's your result.

The original version of this used this construction (March 3 Line of the Day) to copy the list:

  my $pow2 = [map {[@$_]} @$pow];

Then it appended i to each of the elements of $pow2.

As discussed under 3 March, $pow2 = [@$pow] wouldn't have worked here, because if I had used that, then appending i to the elements of $pow2 would also have appended it to the elements of $pow, which is just what I don't want to to.

Actually, though, this code didn't make it into the final version. I realized that it was silly to copy $pow2 and then loop over it a second time to add the i's, so the line above evolved to this:

  $pow2 = [map {[$first,@$_]} @$pow];

Then I decided that I didn't like the order of the elements in the resulting list. The sublists were representing various choices of options, with a missing item indicating a default, and I wanted the program to consider the more specific sets of options before it tried the ones that implied defaults. My function was returning ((), (3), (2), (2, 3), (1), (1, 3), (1, 2), (1, 2, 3)) but I wanted (1, 2, 3) to be first. I would have preferred the list to have the order ((1, 2, 3), (2, 3), (1, 3), (3), (1, 2), (2), (1), ()). So instead of constructing two lists and appending them, I constructed one list with the modified and unmodified elements interleaved:

  [map {[$first,@$_], $_} @$pow];

I didn't need to assign to the variable $pow2 any more, because this map constructs the correct fnial result, including appending i as appropriate.

Then I realized (rather sheepishly) that for my application, I didn't want to make sublists by deleting elements, but rather by replacing them with the empty string:

  [map {[$first,@$_],['',@$_]} @$pow];

Here's the powerset function that appears in the program right now:

sub powerset {
  return [[]] if @_ == 0;
  my $first = shift;
  my $pow = &powerset;
  [map {[$first,@$_],['',@$_]} @$pow];

I considered eliminating $pow by replacing the last two lines with this:

  [map {[$first,@$_],['',@$_]} @{&powerset}];

But I decided that that was silly.

3 March, 1998

Wow, long delay! I didn't write any code for weeks---I was too busy with a lot of other nonsense. But then I had to write a new feedback program for a client's web site, and I came up with this beauty:

  my $pow2 = [map {[@$_]} @$pow];

What does this do? $pow is a reference to a list. This line makes a copy of the list.

Why didn't I use $pow2 = [@$pow]? Because in this case, $pow was a list of lists, and I wanted to be able to modify the lists in the copy without altering $pow. Suppose I had done

  my $pow2 = [@$pow];
  push @{$pow2->[0]}, 'new item';

to insert a new item into the first list in $pow2. Because the elements of @$pow2 are references to lists, and they're references to the same lists that are in @$pow1, I would have inserted 'new item' into the first element of @$pow1 as well!

That wasn't what I wanted here. The line I used copies each of the lists in the lists of lists, makes a new list of the copies, and assigns it to $pow2.

Why was I doing this? Come back tomorrow.

Return to: Universe of Discourse main page | What's new page | Perl Paraphernalia | Line of the Day