Anagram

Alex Martelli aleax at aleax.it
Thu Jan 24 13:00:43 CET 2002


"Joseph A Knapka" <jknapka at earthlink.net> wrote in message
news:3C4ED1A5.56CE7B60 at earthlink.net...
    ...
> # This one allows you to pick an anagram from anywhere
> # in the sequence. <Ana> is the index of the anagram to
> # pick from the possible anagrams of <lis>.
> def pickana(lis,ana):
>     if not lis:
>         return []
>     idx1 = int(ana/fact(len(lis)-1))
>     which1 = lis[idx1]
>
>     # Still annoying. Can I "comprehend" this somehow?
>     llis = lis[:]
>     del llis[idx1]

Not sure what you mean.  "get the idx1-th element
from list llis and remove it from the list too"
is llis.pop(idx1); is that what you mean by
"comprehend"?

>     rest = pickana(llis,ana%fact(len(lis)-1))
>     return [which1] + rest

Or, non-recursively...:

def pickanagram(lis, anaind):
    result = []
    llis = lis[:]
    while llis:
        anaind, indx = divmod(anaind, len(llis))
        result.append(llis.pop(indx))
    return result

I don't think the fact() calls in your pickana are
correct -- I think you need a divmod(ana,len(lis))
in your version too (or equivalently separate uses
of operators / and % as you have, but divmod seems
clearer to me in this case).


> I'm sure some people here could do any of the above
> with two-liners; I'd be interested to see those!

pickanagram seems nice & readable, but if squeezing
functionality into fewer statements were the goal,
something might be feasible.  Usual disclaimers: I
am *NOT* advocating the following as good, or even
halfway sensible, Python programming, just playing
with Joe's "challenge":-).


Basically, the body of pickanagram is CLOSE to a list
comprehension (a single statement) plus the copy of
lis (to make pickanagram nondestructive on its argument!);
this suggests we need to check if we CAN indeed make it
into a list-comprehension.  Moving closer:

def pick1(lis, anaind):
    llis = lis[:]
    result = []
    for i in range(len(lis), 0, -1):
        anaind, indx = divmod(anaind, i)
        result.append(llis.pop(indx))
    return result

This is now very close to list-comprehension form
    result = []
    for <whatever1>
        result.append(<whatever2>)
except for the rebinding of anaind in the
first statement of the for's body, which a list
comprehension can't do *directly*.  HOWEVER, a
list comprehension CAN be nested:
    result = []
    for <whatever1>
        for <whatever2>
            result.append(<whatever3>)
and a for-statement (or for-clause in a lc) IS
able to re-bind variables!  So...:

def pick2(lis, anaind):
    llis = lis[:]
    result = []
    for i in range(len(lis), 0, -1):
        for anaind, indx in [divmod(anaind, i)]:
            result.append(llis.pop(indx))
    return result

and now we ARE in list-comprehensionable form, so:

def pick3(lis, anaind):
    llis = lis[:]
    return [llis.pop(indx)
        for i in range(len(lis), 0, -1)
        for anaind, indx in [divmod(anaind, i)]]

Well, it's now two LOGICAL lines.  So, it's just an
issue of squeezing down whitespace and name lengths:

def pick4(lis, i):
 l = lis[:]
 return[l.pop(j)for k in range(len(l),0,-1)for i,j in[divmod(i,k)]]

and we can, if need be, squeeze a couple more characters
by giving up divmod too:

def pick5(lis, i):
 l = lis[:]
 return[l.pop(j)for k in range(len(l),0,-1)for i,j in[(i/k,i%k)]]

Now THIS is what I mean by "obsessed with shortening one's code"...


Hmmm -- I don't think I'll sign *THIS* post...!!!






More information about the Python-list mailing list