It Slices, It Splices, It Makes Fries

By: Jeff Boes

Welcome to the first of my series of beginner's help columns!

By way of introduction: I am Jeff Boes, an independent consultant and freelance CGI programmer. I've been a professional programmer for 20 years, and a Perl programmer for just about 5 years. Once upon a time I even did a little teaching at the college level, breaking in brand-new programmers (although at that long-past moment, Perl was hardly even a gleam in Larry's eye). If you'd like to learn more about me, check my professional web page. You can contact me at this address, and I invite you to do so if you have questions or comments about these articles.

As a second point of introduction, let me rush to explain that I'm not a Perl Guru (I only claim "experienced programmer" status!), so if I present something here that jars with what you know to be true about Perl, it's just possible that I'm dead wrong! In that case, I welcome your corrections and will make sure that they get printed here. (It's also possible that the explanation was intentionally simplified for the audience, so bear that in mind before you skewer me!)

I'd like to thank the editors of PerlMonth for this opportunity to 'show off' a bit, and for the complement they paid me by selecting me to write this column.

Who Are You?

Now, let's get started: first, some assumptions about my audience. I assume that if you found your way to PerlMonth, that you are already a programmer of some skill, perhaps in some language other than Perl, but that you are completely comfortable with concepts like 'variable', 'expression', 'statement', 'function', loop', and so on. We aren't going to cover Programming 101 here.

Next, I assume that you have a passing familiarity with Unix, simply because there are so many Unix concepts embedded in Perl. (Perl is a portable language and runs on many non-Unix operating systems, but in some sense it does so by presenting a 'Unix-face' to the programmer no matter what the underlying operating system really is!) We will assume Unix as the underlying operating system, and you're on your own if you need to make adjustments.

Last, my guess is that the majority of programmers flocking to learn Perl are doing so because of the Web, and because of CGI (not that there aren't a hundred other reasons to learn and use Perl, but Web/CGI seems to be the most prevalent right now!). So my examples and topics will be chosen to highlight ways to use Perl for CGI work, but not exclusively if another example is more valid.

So, let's get started! For this column, since I've used up so much of your time explaining where I'm at, we'll just dive right in with some short nuggets of interest. Next time we'll explore a single topic in more detail and depth.

Splice Is Nice

Perl syntax has one thing that challenges the new Perl programmer: a lot of detail when it comes to referencing data structures. Arrays, scalars, hashes, typeglobs: all ways to speaking to your data through code. One that seems to mystify new programmers is the SLICE:

Consider a sequence of values from a data structure. If your data is a list of values, that has some ordering:

1, 2, 3, 5, 8, 13, 21

then we can talk about sub-sequences of this data: "the first three elements", "the last two elements", "every other element" and so on.

Perl naturally supports expressing such sequences through what are called "slices". A slice is a list of values, and can appear in a list context (that is, wherever you might use an array name). So, for our example values above:

@data = (1, 2, 3, 5, 8, 13, 21);

@first_three = @data[0..2];     # The first three elements
@last_two = @data[5,6];         # Last two
@evens = (0,2,4,6);
@every_other = @data[@evens];   # and so on
It's important to recognize the syntax here: the array name '@data' appears to let Perl know we're talking about a list of values. The subscripting syntax is itself a list of values, and can be various flavors:

Okay, take a deep breath if that last one caught you offguard. From the inside out:

0..$#data

is the sequence of array indexes into @data, because Perl arrays start at 0 and end at $#{arrayname}.

$_ % 2

is a little test of the $_ variable to see if it's odd, because '%' is the 'modulus' or 'mod' or 'remainder' operator: if $_ is odd, then '$_ % 2' will be '1', else it will be '0'.

grep()

filters the 0..$#data values to give us the odd numbers. Specifically, grep applies the test in the first parameter to each individual element in the rest of its arguments. Any that pass the test are kept, the others discarded. (They aren't removed from the list, they just disappear from the expression.)

However, as pointed out in the excellent book "Effective Perl Programming" by Hall & Schwartz, you must be careful not to use a slice when you mean an element. It's so easy to type

... @data[0] ...

when you meant

... $data[0] ...

and in many cases, the two expressions will turn out to have the same meaning. But not always.

Let's look at slices from the other side of the equals sign. A slice is not just a shortcut way of expressing some values from your array. It's also an elegant way of assigning to those parts of the array. For example, to replace the first element of our '@data' array, we might say

$data[0] = 'secret';

But to replace two elements, rather than say

$data[0] = 'secret';
$data[1] = 'weapon';

we can instead say:

@data[0,1] = qw(secret weapon);

So much more expressive, especially if our toy example of two elements becomes instead 20 or so:

@words[0..15] = qw(Now is the time for all good men to come to the aid of their country);

Back to $data[0] versus @data[0]. The difference here is when a slice appears on the left side of an assignment, then the right side has to be a list of values. Even if the slice is just one item long. Sometimes, Perl makes a guess about what the right-hand side has to be to make sense with your left-hand side, and if you give it the wrong clues, you can really hose yourself. Example: localtime.

localtime is a function that returns the time of day. But it does so in two very different ways depending on context. If called in a scalar context, you get the time as a string, e.g.,

print scalar(localtime);
Mon Feb 14 16:36:41 2000

(scalar() is just a function that forces Perl to evaluate something in scalar context.) But if you call it in list context, you get a very different result.

print localtime;
4136161411001440

Wha-huh? This would perhaps make more sense if we insert some punctuation:

print join(',', localtime);
41,36,16,14,1,100,1,44,0

We won't delve into what this all means, just that it's different in list context. (You can look it up yourself: "perldoc -f localtime" from your nearest available command prompt.)

So, armed with this we can now explain why

@time[1] = localtime;

and

$time[1] = localtime;
will do entirely different things. (Exercise for the reader: go write a Perl script to do both of these and display the results with a print statement.)

Okay, enough about slices. Now we'll talk about splices! splice() is one of Perl's mysterious functions that involve lists and slices and all this stuff we've talked about. splice() is a way of pulling out a sequence of values from a list (hey, it's a slice!) and replacing it with another sequence of values. Oh, big deal, we've done that already, right?

@data[0,1] = qw(secret weapon);

Ah, but with splice() we can put in more or less data than we took out! With our simple slice assignment we have to supply the same number of values on the right as we referred to on the left. (Actually, not true: we can short-change the left, but the extra left-side elements get undefined values, and we can oversupply the left, but the extras are ignored. Try it:

@data[0,1,2] = qw(secret weapon);
@data[0,1] = qw(secret weapon of mass destruction);

and see what happens to @data.)

Now splice comes along:

splice(<array>, <start>, <length>, <replacements>);

splice(@data, 0, 2, qw(secret weapon of mass destruction));

This snips off the first two elements from @data, and shoves the phrase onto the front of the list.

That's about all we have time and room for this month. Next month I plan to discuss Perl debugging techniques, especially in the context of CGI programming. If you liked this column, or hated it, or even felt neutral about it, please let me know.