[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

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

david jensen

3/16/2010 12:57:00 PM

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
10 Answers

david jensen

3/16/2010 1:06:00 PM

0

.... 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

3/16/2010 1:48:00 PM

0



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

3/16/2010 2:28:00 PM

0

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)

Paul Rubin

3/16/2010 3:10:00 PM

0

david jensen <dmj.ccc@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

3/16/2010 3:22:00 PM

0

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

david jensen

3/16/2010 4:32:00 PM

0

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

3/16/2010 4:55:00 PM

0

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

3/16/2010 7:08:00 PM

0

david jensen wrote:
> 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

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

3/16/2010 8:27:00 PM

0

>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

3/17/2010 2:30:00 PM

0



Michael Torrie wrote:
> david jensen wrote:
> > 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
>
> 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.

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