converting from perl: variable sized unpack

Thomas Wouters thomas at xs4all.net
Sun Jul 15 07:12:55 EDT 2001


On Fri, Jul 13, 2001 at 11:28:39PM -0400, Barry A. Warsaw wrote:

> >>>>> "RS" == Roy Smith <roy at panix.com> writes:
> 
>     RS> What's the best way to translate this perl snippet into
>     RS> python:
> 
>     RS>  ($f1, $f2, $f3, $f4, $f5, $f6) = split ('\s+', $line);
> 
>     RS> The obvious translation would be:
> 
>     RS>  [f1, f2, f3, f4, f5, f6] = string.split (line)
> 
>     RS> but the problem is that line may have fewer than 6 fields on
>     RS> it.  Perl handles this smoothly by leaving the "extra"
>     RS> variables undefined, whereas python raises a ValueError
>     RS> exception.  What I'd like to do is have the extra field
>     RS> variables left as null strings.

> Python doesn't work this way, but I've been thinking about writing a
> PEP to make sequence unpacking less strict.

Oh, cool. Here are some ideas for you, from my second-most favourite
language, LambdaMOO. It's the language that learned me programming, and laid
the groundwork for my love of Python -- it's an amazingly Pythonic language,
probably because it was developed at PARC Xerox to teach basic OOP concepts.
I strongly suspect either ABC or Python influenced MOO (or viceversa) but
no-one ever came forward to admit it :)

Anyway, MOO lists are created using '{}'s like in ABC, and indexing starts
with 1, not 0. And MOO lists can't be added with '+', so there is a special
'splice' operator that 'unpacks' a list in-place: the MOO expression

     caps = {"A", "B"};
     l = {"a", "b", @caps, "c", "d"}

Is the same as the Python 

     caps = ["A", "B"]
     l = ["a", "b"] + caps + ["c", "d"]

And this '@' syntax comes back in the 'sequence unpacking' MOO has. The
LambdaMOO Programmers Manual explains this so nicely, you could almost copy
this into your PEP :-)

>From ftp://ftp.lambda.moo.mud.org/pub/MOO/ProgrammersManual.html (also
available in .tex, .dvi, .ps and .txt from the same location):

"""
Spreading List Elements Among Variables

It is often the case in MOO programming that you will want to access the
elements of a list individually, with each element stored in a separate
variables. This desire arises, for example, at the beginning of almost every
MOO verb, since the arguments to all verbs are delivered all bunched
together in a single list. In such circumstances, you could write statements
like these:

first = args[1];
second = args[2];
if (length(args) > 2)
  third = args[3];
else
  third = 0;
endif

This approach gets pretty tedious, both to read and to write, and it's prone
to errors if you mistype one of the indices. Also, you often want to check
whether or not any extra list elements were present, adding to the tedium.

MOO provides a special kind of assignment expression, called scattering
assignment made just for cases such as these. A scattering assignment
expression looks like this:

{target, ...} = expr

where each target describes a place to store elements of the list that
results from evaluating expr. A target has one of the following forms:

variable

This is the simplest target, just a simple variable; the list element in the
corresponding position is assigned to the variable. This is called a
required target, since the assignment is required to put one of the list
elements into the variable.

?variable

This is called an optional target, since it doesn't always get assigned an
element. If there are any list elements left over after all of the required
targets have been accounted for (along with all of the other optionals to
the left of this one), then this variable is treated like a required one and
the list element in the corresponding position is assigned to the variable.
If there aren't enough elements to assign one to this target, then no
assignment is made to this variable, leaving it with whatever its previous
value was. 

?variable = default-expr

This is also an optional target, but if there aren't enough list elements
available to assign one to this target, the result of evaluating
default-expr is assigned to it instead. Thus, default-expr provides a
default value for the variable. The default value expressions are evaluated
and assigned working from left to right after all of the other assignments
have been performed.

@variable

By analogy with the @ syntax in list construction, this variable is assigned
a list of all of the `leftover' list elements in this part of the list after
all of the other targets have been filled in. It is assigned the empty list
if there aren't any elements left over. This is called a rest target, since
it gets the rest of the elements. There may be at most one rest target in
each scattering assignment expression.

If there aren't enough list elements to fill all of the required targets, or
if there are more than enough to fill all of the required and optional
targets but there isn't a rest target to take the leftover ones, then E_ARGS
is raised.

Here are some examples of how this works. Assume first that the verb
me:foo() contains the following code:

b = c = e = 17;
{a, ?b, ?c = 8, @d, ?e = 9, f} = args;
return {a, b, c, d, e, f};

me:foo(1)                        error--> E_ARGS
me:foo(1, 2)                     => {1, 17, 8, {}, 9, 2}
me:foo(1, 2, 3)                  => {1, 2, 8, {}, 9, 3}
me:foo(1, 2, 3, 4)               => {1, 2, 3, {}, 9, 4}
me:foo(1, 2, 3, 4, 5)            => {1, 2, 3, {}, 4, 5}
me:foo(1, 2, 3, 4, 5, 6)         => {1, 2, 3, {4}, 5, 6}
me:foo(1, 2, 3, 4, 5, 6, 7)      => {1, 2, 3, {4, 5}, 6, 7}
me:foo(1, 2, 3, 4, 5, 6, 7, 8)   => {1, 2, 3, {4, 5, 6}, 7, 8}

Using scattering assignment, the example at the begining of this section
could be rewritten more simply, reliably, and readably:

{first, second, ?third = 0} = args;

It is good MOO programming style to use a scattering assignment at the top
of nearly every verb, since it shows so clearly just what kinds of arguments
the verb expects.
"""

I think I would really enjoy seeing

  fromaddr, toaddr, subject="(no subject)", contenttype="text/plain" = f()

(though I know that's syntactically impossible, *snif* ;)
and

  user, domain, @aliases = getinfo()

in Python, though I doubt I ever will :)

-- 
Thomas Wouters <thomas at xs4all.net>

Hi! I'm a .signature virus! copy me into your .signature file to help me spread!




More information about the Python-list mailing list