Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Searching for most pythonic/least stupid way to do something simple

3 views
Skip to first unread message

david jensen

unread,
Mar 16, 2010, 8:57:05 AM3/16/10
to
Hi all,

This may be a complete brainfart, but it's been puzzling me for a day
or two (!).
Sorry for not describing "something" in the subject, but it's hard to
describe succinctly:

I have a short list of non-zero positive integers (say
myList=[2,5,8,3,5]). I need to return five lists of non-negative
numbers, such that for five different "share sizes", myList[0] and
myList[1] will share twice the smaller amount...

def getOutcomes():
outcomes=[]
if myList[0]<=myList[1]:
amountToShare=2*myList[0]
remainder=myList[1]-myList[0]
outcome.append((amountToShare, remainder)+myList[2:]) #
shares are (100%, 0)
outcome.append((amountToShare*0.75, remainder
+amountToShare*0.25)+myList[2:]) #shares are (75%, 25%), and exactly
the same for (50%,50%), (25%, 75%), and (0,100%)
...
...
return outcomes

i.e. for the above myList, outcomes=[[4,1,8,3,5], [3,2,8,3,5],
[2,5,8,3,5],[1,6,8,3,5],[0,7,8,3,5]]

if myList[0]>myList[1], i want exactly the opposite to happen (i.e.,
just switching what happens to positions 0 and 1)

Obviously, i can just write the code again, in an else, switching
indices 0 and 1. Or, I could just have a test at the beginning, switch
them if they are in the order "big, small", and then switch them again
at the end in a list comprehension. Both ideas seem terribly silly,
and there must be an obvious way to do it that I'm not seeing.

any help?

many thanks

dmj

david jensen

unread,
Mar 16, 2010, 9:05:30 AM3/16/10
to
... and of course i screwed up my outcomes... that should read
outcomes=[[4,3,8,3,5],[3,4,8,3,5],[2,5,8,3,5],[1,6,8,3,5],[0,7,8,3,5]]

nn

unread,
Mar 16, 2010, 9:48:11 AM3/16/10
to

david jensen wrote:
> ... and of course i screwed up my outcomes... that should read
> outcomes=[[4,3,8,3,5],[3,4,8,3,5],[2,5,8,3,5],[1,6,8,3,5],[0,7,8,3,5]]

For starters:

def getOutcomes(myList=[2,5,8,3,5]):
low_id = int(myList[0]>myList[1])
amountToShare = 2*myList[low_id]
remainder = myList[not low_id]-myList[low_id]
tail=myList[2:]
outcomes = [[amountToShare*perc, remainder+amountToShare*(1-perc)]
+ tail
for perc in (1.0, 0.75, 0.5, 0.25, 0.0)]
return outcomes

Gerard Flanagan

unread,
Mar 16, 2010, 10:28:17 AM3/16/10
to pytho...@python.org
david jensen wrote:
> ... and of course i screwed up my outcomes... that should read
> outcomes=[[4,3,8,3,5],[3,4,8,3,5],[2,5,8,3,5],[1,6,8,3,5],[0,7,8,3,5]]
>
>
>

abstracting the given algorithm:

def iterweights(N):
d = 1.0/(N-1)
for i in xrange(N):
yield i*d, (N-1-i)*d

def iterparts(x0, x1, N):
a = min(x0, x1)
b = max(x0, x1)
s = 2 * a
t = b - a
for m, n in iterweights(N):
if a == x0:
yield s*m, s*n+t
else:
yield s*n+t, s*m

for p in iterparts(2, 5, 5):
print p

print

for p in iterparts(5, 2, 5):
print p

(0.0, 7.0)
(1.0, 6.0)
(2.0, 5.0)
(3.0, 4.0)
(4.0, 3.0)

(7.0, 0.0)
(6.0, 1.0)
(5.0, 2.0)
(4.0, 3.0)
(3.0, 4.0)

david jensen

unread,
Mar 16, 2010, 11:21:41 AM3/16/10
to
Thank you both very much!

Yeah: it was a total brainfart on my part: nn's solution should have
been obvious.

As the general solution, i like your approach, Gerard, but I think
I'll stick to nn's, the one i should have written.

Again, thank you both!

dmj

Paul Rubin

unread,
Mar 16, 2010, 11:09:37 AM3/16/10
to
david jensen <dmj...@gmail.com> writes:
> Obviously, i can just write the code again, in an else, switching
> indices 0 and 1. Or, I could just have a test at the beginning, switch
> them if they are in the order "big, small", and then switch them again
> at the end in a list comprehension. Both ideas seem terribly silly,
> and there must be an obvious way to do it that I'm not seeing.

Generally when faced with this kind of question, see if you can
use the built-in min and max functions:

def getOutcomes():
outcomes=[]
smaller = min(myList[0], myList[1])
bigger = max(myList[0], myList[1])
amountToShare=2*smaller
remainder = bigger - smaller
...

david jensen

unread,
Mar 16, 2010, 12:31:36 PM3/16/10
to
Thanks Paul, but i don't immediately see how that helps (maybe I'm
just being dense)... nn's solution, though i initially thought it
worked, actually has a similar problem:

intended:
>>> print getOutcomes([3,4,5,5])
[[6, 1, 5, 5], [4.5, 2.5, 5, 5], [3, 4, 5, 5], [1.5, 5.5, 5, 5], [0,
7, 5, 5]]
>>> print getOutcomes([4,3,5,5])
[[7, 0, 5, 5], [5.5, 1.5, 5, 5], [4, 3, 5, 5], [2.5, 4.5, 5, 5], [1,
6, 5, 5]]

nn's solution:
>>> print getOutcomesNN([4,3,5,5])
[[6.0, 1.0, 5, 5], [4.5, 2.5, 5, 5], [3.0, 4.0, 5, 5], [1.5, 5.5, 5,
5], [0.0, 7.0, 5, 5]]


it's obvious why this is happening (the list comprehension assumes
that index 0 is, after all, index 0), but not immediately obvious how
to fix it, except by going with gerard's solution or doing what i did
in the beginning: flip them if they're in the wrong order, remember,
and flip them again at the end.

dmj

david jensen

unread,
Mar 16, 2010, 12:54:57 PM3/16/10
to
of course, changing nn's to:

def getOutcomes(myList=[2,5,8,3,5]):
low_id = int(myList[0]>myList[1])
amountToShare = 2*myList[low_id]
remainder = myList[not low_id]-myList[low_id]
tail=list(myList[2:])

outcomes = [[amountToShare*perc, remainder+amountToShare*(1-perc)]+
tail for perc in (1.0, 0.75, 0.5, 0.25, 0.0)] if not low_id else
[[remainder+amountToShare*perc, amountToShare*(1-perc)]+ tail for perc

in (1.0, 0.75, 0.5, 0.25, 0.0)]
return outcomes


works, just hides the ugliness in a more compact form

Michael Torrie

unread,
Mar 16, 2010, 3:07:38 PM3/16/10
to pytho...@python.org

If Gerard's code works, I would consider it far superior to your code
here. Pythonic does not necessarily mean short and ugly, nor does it
mean that you have to always use list comprehensions. Having a
readable algorithm that's easy to follow in the future is a far better
way than trying to use python's cool features to compact the code to as
small and unreadable section as possible.

I used to use list comprehension all the time, but I've found that often
an explicit for loop is a much better solution in terms of
maintainability. Especially when you start seeing nested comprehensions
such as you have here.

david jensen

unread,
Mar 16, 2010, 4:27:19 PM3/16/10
to
>If Gerard's code works, I would consider it far superior to your code
>here. Pythonic does not necessarily mean short and ugly

yes, I agree... and in my script i'm using something very like
Gerard's (thanks again, Gerard). I just posted the corrected version
of nn's because the original solved only half the problem.

thanks, all

dmj

nn

unread,
Mar 17, 2010, 10:29:31 AM3/17/10
to

To be fair, that list comprehension was unnecessary complicated. The
following version does the same thing and still looks pretty
reasonable IMHO:

def getOutcomes(myList=[2,5,8,3,5]):
low_id = int(myList[0]>myList[1])

high_id = not low_id
smaller = myList[low_id]
bigger = myList[high_id]
amountToShare = 2*smaller
remainder = bigger-smaller
remain0 = low_id*remainder
remain1 = high_id*remainder
tail = list(myList[2:])
percents = (1.0, 0.75, 0.5, 0.25, 0.0)
outcomes = [[remain0+amountToShare*perc, remain1+amountToShare*(1-
perc)]
+tail for perc in percents]
return outcomes

0 new messages