Do this as a list comprehension?

Mensanator mensanator at aol.com
Sat Jun 7 17:42:16 EDT 2008


On Jun 7, 1:16�pm, John Salerno <johnj... at gmailNOSPAM.com> wrote:
> Mensanator wrote:
> > Surely enumerate() wasn't added to Python with no intention of
> > ever being used.
>
> I see your reasons for preferring enumerate over zip,

It's not that I prefer it, it's that you specifically
asked a list comprehension and I gave you one. I use
zip when I need to, but I would never use it with a
range function unless I'm deliberately creating a
subset (where you certainly don't want to use enumerate).

> but I'm wondering if using enumerate this way isn't
> a little hackish or artificial.

I wouldn't say so. An index can be part of the
data structure - a part that doesn't have to be
stored, it's implied. And just like everything
else, this entails a certain responsibility on
your part that the data you are storing is indexed
correctly.

> Isn't the point of enumerate to get the index
> of a specific item, as well as that item itself?

I would say that's the main point but not the
only point. It's handy when you have multiple
iterables that are related by their position.

But even in a single iterable the position can
be part of the data structure.

> It seems like arbitrarily altering the index (i+8) is
> an abuse of enumerate's true purpose.

It's not an abuse because we aren't using (i+8) as
an index, it's simply data. Your point would be
more valid if we turned around and used (i+8) as
an index into another iterable. Not that we couldn't
do it, but we would have to be concerned about
"out of range" errors.

>
> Does this make any sense,

It makes as much sense as range(8,19).

> or is it generally acceptable to use enumerate
> like this?

I use enumerate frequently, and often the index
isn't used as an index. Let me ive you an example:

Say I have a list sv=[1,2,3,4].

What does this list represent? It represents
the contiguous n/2 operations in a sequence from
the Collatz Conjecture. If you know that, you should
immediately ask why I'm not counting the 3n+1
operations? Ah, but I AM counting them. In the CC,
every block of contiguous n/2 operations is seperated
by EXACTLY one 3n+1 operation. So the list structure
implies that every number is preceeded by a single
3n+1 operation. Thus, I don't have to waste memory
counting the 3n+1 blocks because the count is ALWAYS
one and ALWAYS precedes each number in the list.

Now, there's a real nice function you can derive from
any given list. The function is always the same but
it contains three constants (X,Y,Z) that have to be
derived from the list. X is simply 2**sum(sv). Y is
simply 3**len(sv).

But Z is much trickier. For every term in sv, there
is a term that's a power of 3 times a power of 2,
all of which must be summed to get Z.

>>> sv =[1,2,3,4]
>>> b = [2**i for i in sv]
>>> b
[2, 4, 8, 16]

Here I'm simply using the elements of sv to compute
the powers of 2. Nothing special here.

>>> c = [3**i for i,j in enumerate(sv)]
>>> c
[1, 3, 9, 27]

Here, I'm using the index to find the powers of 3.
Note that j isn't being used. But, I can then
combine them thusly:

>>> d = [3**i*2**j for i,j in enumerate(sv)]
>>> d
[2, 12, 72, 432]

Here the index isn't being used as an index at all,
it's an integral part of the data structure that
can be extracted along with the physical contents
of the list by using enumerate.

Actually, that formula doesn't give the right
answer, it was just a demo. The true formula
would be this:

>>> f = [3**i*2**(sum(sv[:len(sv)-i-1])) for i,j in enumerate(sv)]
>>> f
[64, 24, 18, 27]
>>> Z = sum(f)
>>> Z
133

Although in my actual collatz_functions library I use

for i in xrange(svll,-1,-1):
  z = z + (TWE**i * TWO**sum(itertools.islice(sv,0,svll-i)))

Which I csn use to check the above answer.

>>> import collatz_functions as cf
>>> xyz = cf.calc_xyz(sv)
>>> xyz[2]
mpz(133)

Maybe I've gone beyond enumerate's true purpose,
but I find it hard to imagine the designers not
anticipating such useage.



More information about the Python-list mailing list