Re: Speed of Nested Functions & Lambda Expressions

2007-12-06 Thread Terry Jones
[Referring to the thread at
 http://mail.python.org/pipermail/python-list/2007-October/463348.html
 with apologies for top posting (I don't have the original mail)]

Duncan Booth wrote:

> You can use Python's bytecode disassembler to see what actually gets 
> executed here:
> 
> >>> def fn_outer(v):
> a=v*2
> def fn_inner():
> print "V:%d,%d" % (v,a)
> 
> fn_inner()
> 
> >>> import dis
> >>> dis.dis(fn_outer)
>   2   0 LOAD_DEREF   1 (v)
[snip]


> When you execute the 'def' statement, the two scoped variables a and v 
> are built into a tuple on the stack, the compiled code object for the 
> inner function is also pushed onto the stack and then the function is 
> created by the 'MAKE_CLOSURE' instruction. This is then stored in a 
> local variable (STORE_FAST) which is then loaded and called.
> 
> So the function definition is pretty fast, BUT notice how fn_inner is 
> referenced by STORE_FAST/LOAD_FAST whereas a and v are referenced by 
> LOAD_DEREF/STORE_DEREF and LOAD_CLOSURE.
> 
> The code for fn_inner also uses LOAD_DEREF to get at the scoped 
> variables:

I'd like to understand what it is that triggers the use of LOAD/STORE_DEREF
versus LOAD/STORE_FAST. This seems dependent on how fn_inner above uses the
a and v variables. If I change the print "V:%d,%d" % (v,a) line to be
something like (a, v) = (v, a) or do other simple uses and assignments to a
and v, LOAD/STORE_FAST is used. It seems that it's the use of print (in
this case) that triggers the use of LOAD/STORE_DEREF in the bytecode.

Can anyone shed more light on what in general causes the switch to
LOAD/STORE_DEREF?

I find it a bit disconcerting that the simple presence of a nested function
(even if never used) may trigger significant slowdown in variable access
for the rest of the code in the containing function. I'd be happy to know
how/when this happens. I know I probably shouldn't care, but I do.

Thanks,
Terry Jones
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Speed of Nested Functions & Lambda Expressions

2007-12-07 Thread Terry Jones
>>>>> "Duncan" == Duncan Booth <[EMAIL PROTECTED]> writes:
Duncan> Terry Jones <[EMAIL PROTECTED]> wrote:
>> Duncan Booth wrote:

Duncan> You'll kick yourself for not seeing it.
Duncan> If you changed fn_inner to:

Duncan> def fn_inner():
Duncan> a, v = v, a

Duncan> then you also changed 'a' and 'v' into local variables.
Duncan> LOAD/STORE_FAST is used to access local variables, LOAD/STORE_DEREF
Duncan> are used to access variables in an outer scope.

Argh, yes :-)  [Cue background sound of kicking self]

Thanks.

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Finding overlapping times...

2007-12-13 Thread Terry Jones
Hi Breal

> I have a list that looks like the following
> [(10, 100010), (15, 17), (19, 100015)]
> 
> I would like to be able to determine which of these overlap each
> other.  So, in this case, tuple 1 overlaps with tuples 2 and 3.  Tuple
> 2 overlaps with 1.  Tuple 3 overlaps with tuple 1.
> 
> In my scenario I would have hundreds, if not thousands of these
> ranges.  Any nice pythonic way to do this?

This may be of no help, but

In 1995 I wrote a C library to do some stuff like this. You're welcome to
the code. It might give you some ideas, or you might want to wrap it for
Python. If you did that, and had additional energy, you could release the
result to the Python community.

As someone said, how/what to do depends what you want to do with the
resulting data structure once you've processed the inputs.

In my case I wanted to be able to read in a large number of ranges and be
able to answer questions like "is X in the range?". I'm pretty sure I made
it possible to add ranges to ignore (i.e., that punch holes in an existing
range). I'm also pretty sure I'd have made this run in n log n time, by
first sorting all the lower bounds (and maybe sorting the upper ones too)
and then walking that list. 

One use case is e.g., for a printer program command line:

  print --inc-pages 40-70 --exc-pages 51-52 --inc-pages 100-120

which could use the library to step through the range of pages in a doc and
easily check which ones should be printed.  There are lots of other uses.
Lookup time on those queries was probably log n where n is the resulting
number of range fragments once the processing (merging/splitting) of the
command line was done.

I don't remember, but it took me several days to write the code, so it
wasn't completely trivial.

I can't offer help beyond the code I'm afraid - I have too much other stuff
going on.

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: How to generate pdf file from an html page??

2007-12-19 Thread Terry Jones
> "Grant" == Grant Edwards <[EMAIL PROTECTED]> writes:
Grant> On 2007-12-19, abhishek <[EMAIL PROTECTED]> wrote:
>>> > Hi everyone, I am trying to generate a PDF printable format file from
>>> > an html page. Is there a way to do this using python. If yes then
>>> > which library and functions are required and if no then reasons why it
>>> > cant be done.
>>> 
>>> Here's one way:
>>> 
>>> --html2pdf.py-
>>> #!/usr/bin/python
>>> import os,sys
>>> 
>>> inputFilename,outputFilename = sys.argv[1:3]
>>> 
>>> os.system("w3m -dump %s | a2ps -B --borders=no | ps2pdf - %s" % 
>>> (inputFilename,outputFilename))

Note that this is highly insecure. outputFilename could be passed e.g., as

  /tmp/file.pdf; rm -fr /home/abhishek

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: getting n items at a time from a generator

2007-12-27 Thread Terry Jones
> "Kugutsumen" == Kugutsumen  <[EMAIL PROTECTED]> writes:
Kugutsumen> On Dec 27, 7:07 pm, Paul Hankin <[EMAIL PROTECTED]> wrote:
>> On Dec 27, 11:34 am, Kugutsumen <[EMAIL PROTECTED]> wrote:
>> 
>> > I am relatively new the python language and I am afraid to be missing
>> > some clever construct or built-in way equivalent to my 'chunk'
>> > generator below.

Kugutsumen> Thanks, I am going to take a look at itertools.  I prefer the
Kugutsumen> list version since I need to buffer that chunk in memory at
Kugutsumen> this point.

Also consider this solution from O'Reilly's Python Cookbook (2nd Ed.) p705

def chop(iterable, length=2):
return izip(*(iter(iterable),) * length)

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Taxicab Numbers

2007-12-27 Thread Terry Jones
> "rgalgon" == rgalgon  <[EMAIL PROTECTED]> writes:

rgalgon> I'm new to Python and have been putting my mind to learning it
rgalgon> over my holiday break. I've been looking over the functional
rgalgon> programming aspects of Python and I'm stuck trying to come up with
rgalgon> some concise code to find Taxicab numbers
rgalgon> (http://mathworld.wolfram.com/ TaxicabNumber.html).

rgalgon> "Taxicab(n), is defined as the smallest number that can be
rgalgon> expressed as a sum of two positive cubes in n distinct ways"

rgalgon> In Haskell something like this could easily be done with:
rgalgon> cube x = x * x * x

rgalgon> taxicab n = [(cube a + cube b, (a, b), (c, d))
rgalgon> | a <- [1..n],
rgalgon> b <- [(a+1)..n],
rgalgon> c <- [(a+1)..n],
rgalgon> d <- [(c+1)..n],
rgalgon> (cube a + cube b) == (cube c + cube d)]

rgalgon> I'm looking for a succinct way of doing this in Python. I've been
rgalgon> toying around with filter(),map(), and reduce() but haven't gotten
rgalgon> anything half-way decent yet.

rgalgon> So, how would you implement this taxicab(n) function in Python?

To give a fairly literal translation of your Haskell, you could just use
this:

def cube(n): return n ** 3

def taxicab(n):
n += 1
return [(cube(a) + cube(b), (a, b), (c, d))
for a in xrange(1, n)
for b in xrange(a + 1, n)
for c in xrange(a + 1, n)
for d in xrange(c + 1, n)
if cube(a) + cube(b) == cube(c) + cube(d)]

If you print the return value of this you get the same results as your
Haskell examples. I added the following to my Python file:

if __name__ == '__main__':
import sys
print taxicab(int(sys.argv[1]))

Then I see 

$ time taxicab.py 20
[(1729, (1, 12), (9, 10)), (4104, (2, 16), (9, 15))]

real0m0.098s
user0m0.046s
sys 0m0.046s


Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: getting n items at a time from a generator

2007-12-29 Thread Terry Jones
Hi Igor

> "Igor" == Igor V Rafienko <[EMAIL PROTECTED]> writes:

>> Also consider this solution from O'Reilly's Python Cookbook (2nd Ed.)
>> p705
>> 
>> def chop(iterable, length=2):
>> return izip(*(iter(iterable),) * length)

Igor> Is this *always* guaranteed by the language to work? Should the
Igor> iterator returned by izip() change the implementation and evaluate
Igor> the underlying iterators in, say, reverse order, the solution would
Igor> no longer function, would it? Or is it something the return value of
Igor> izip() would never do?

Igor> (I am just trying to understand the solution, not criticize it. Took
Igor> a while to parse the argument(s) to izip in the example).

I had to look at it a bit too.  I actually deleted the comment I wrote
about it in my own code before posting it here and decided to simply say
"consider" in the above instead :-)

As far as I understand it, you're right. The docstring for izip doesn't
guarantee that it will pull items from the passed iterables in any order.
So an alternate implementation of izip might produce other results. If it
did them in reverse order you'd get each n-chunk reversed, etc.

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Two candies

2008-01-02 Thread Terry Jones
> "Raymond" == Raymond Hettinger <[EMAIL PROTECTED]> writes:
Raymond> * in most apps (except for sparse arrays), the initialization time
Raymond> for an array is dominated by the time spent actually doing
Raymond> something useful with the array (iow, this is an odd place to be
Raymond> optimizing)

This brings to mind an old algorithms chestnut (exercise 2.12 in the 1st
edition of Aho, Hopcroft & Ullman [1]):

If the implementation of an algorithm uses (for simplicity's sake) a square
array to represent its data, why are _all_ such algorithms not necessarily
O(n^2) due simply to the initialization requirement (supposing a model of
computation that counts assignments)?

Terry

[1] 
http://www.amazon.com/Analysis-Algorithms-Addison-Wesley-Information-Processing/dp/0201000296
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Open a List of Files

2008-01-08 Thread Terry Jones
Hi BJ

> > Fredrik Lundh <[EMAIL PROTECTED]> writes:
> > Or in a dict:
> >
> > open_files = {}
> > for fn in ['messages', 'recipients', 'viruses']:
> >open_files[fn] = open(getfilename(fn), 'w')
> 
> I decided that I was just trying to be "too smooth by 1/2" so I fell back
> to ...
> 
> messages = open(os.path.join(host_path,'messages.txt'), 'wb')
> deliveries = open(os.path.join(host_path,'deliveries.txt'), 'wb')
> actions = open(os.path.join(host_path,'actions.txt'), 'wb')
> parts = open(os.path.join(host_path,'parts.txt'), 'wb')
> recipients = open(os.path.join(host_path,'recipients.txt'), 'wb')
> viruses = open(os.path.join(host_path,'viruses.txt'), 'wb')
> esp_scores = open(os.path.join(host_path,'esp_scores.txt'), 'wb')

I think you should revisit this decision.  Something like Fredrik's code is
the way to go.  It has multiple advantages:

 - It's much shorter.
 - It's arguably easier to add/remove to/from.
 - It has less risk of error (much less repetition).
 - It allows your code to later take a string file tag and
   write to that file by looking up its file descriptor in the dict.
 - You can close all open files with a trivial loop.

Also, if you start writing code like Fredrik's instead of like what you
fell back on you'll make yourself a better programmer (in general, not just
in Python).

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Open a List of Files

2008-01-09 Thread Terry Jones
> "Fredrik" == Fredrik Lundh <[EMAIL PROTECTED]> writes:

Fredrik> Seriously, for a limited number of files, the dictionary approach
Fredrik> is mostly pointless; you end up replacing

Fredrik> foo = open("foo")
Fredrik> foo.write(...)

Fredrik> with

Fredrik> somedict["foo"] = open("foo")
Fredrik> somedict["foo"].write(...)

Yes, in some cases. But where you want to do multiple things you can always
pull the file descriptor of of the dict into a local var.

>> - It has less risk of error (much less repetition).

Fredrik> No, it hasn't.  There's more to type when using the files, and you
Fredrik> have *two* things you can misspell; that is, you're replacing a
Fredrik> NameError with either a NameError or a Key Error.

Well I meant less error risk in the context of opening the files. And I'm
happy to get a NameError or KeyError if I make the mistake you describe.
But bad code like this:

messages = open(os.path.join(host_path,'messages.txt'), 'wb')
deliveries = open(os.path.join(host_path,'deliveries.txt'), 'wb')
actions = open(os.path.join(host_path,'deliveries.txt'), 'wb')

doesn't give an error at all, and with all that identical and
semi-identical code it's much less obvious where the error is.

Cut & paste errors like this are pretty common, and they can be quite hard
to find (when they don't result in exceptions), in my experience.

>> - It allows your code to later take a string file tag and
>> write to that file by looking up its file descriptor in the dict.

Fredrik> Instead of allowing your code to take a file object and write to that 
Fredrik> file by writing to that file object?

No, see below:

>> - You can close all open files with a trivial loop.

Fredrik> Ok, this is actually an advantage.  Not that you need to do that very 
Fredrik> often, since Python does it for you.

Yes.  That's partly why I said it would make him a better programmer in
general, not just in Python.

Fredrik> And if you find yourself needing to do this a lot, you can of
Fredrik> course stuff all the output files in a list but *still* use the
Fredrik> variables to refer to the file when writing to them.

Yes, as above.

Fredrik> There is one case where a dictionary of files makes perfect sense, of 
Fredrik> course, and that's when you can associate the file with some *other* 
Fredrik> value that you're *already* using. (Say, a user name or a machine name 
Fredrik> or a severity level or something like that.)

Yes. That's what I meant by my original - badly worded - remark (see above)
that you could take a "string file tag" and use it to look up its file
descriptor.

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Open a List of Files

2008-01-09 Thread Terry Jones
> "BJ" == BJ Swope <[EMAIL PROTECTED]> writes:

I (at least) think the code looks much nicer.

BJ> #Referring to files to write in various places...
BJ> open_files['deliveries'].write(flat_line)
BJ> open_files['deliveries'].write('\n')

If you were doing a lot with the deliveries file at some point, you could
put the file descriptor into a local variable

  deliveries = open_files['deliveries']
  deliveries.write(flat_line)
  deliveries.write('\n')

BJ> #And finally to close the opened files
BJ> for fn in open_files.keys():
BJ> open_files[fn].close()

You don't need "for fn in open_files.keys():", you can just use "for fn in
open_files:", but simpler than that is to just use the dictionary values:

for fn in open_files.values():
fn.close()

Regards,
Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


RE: Some Berkeley DB questions (being maintained? queries?)

2008-01-17 Thread Terry Jones
> "Brian" == Brian Smith <[EMAIL PROTECTED]> writes:
Brian> [EMAIL PROTECTED] wrote:

>> 2. Are there good python libraries for bdb available, that 
>> are being maintained?

Brian> I would like to know the answer to this question too--if you have
Brian> used the pybsddb/bsddb.db module, please share your experience.

I used it a little in mid-2006. I ran into one issue that I resolved by
looking at the C source and fiddling with the Python interface. I sent mail
to the people mentioned in the bsddb README file. One (Barry Warsaw)
answered to say he hadn't looked at the code in a couple of years and was
probably not the person to contact.

>> 4. What are good, stable alternatives?

>From memory, the Chandler project also has an interface to BSD DB, but I
don't remember any details at all.

I'm also interested in any ongoing or planned work on the Python interface.

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: writing Python in Emacs

2008-01-19 Thread Terry Jones
> "Richard" == Richard Szopa <[EMAIL PROTECTED]> writes:

Richard> I am a devoted Emacs user and I write a lot in Python.

Me too.

Richard> I need the following features:

Richard> 1) Tab completion, ideally Slime like. That is, when there's not
Richard> enough letters to unambiguously complete a symbol, I want it to
Richard> show a buffer (w/o taking the focus) w/ the possible
Richard> completions. In an ideal world, it would be able to complete
Richard> fo.ba to foo.bar. I imagine this would require quite tight
Richard> Emacs-Python integration.

I know this is not what you want, but I use hippie expand (M-/) to cycle
through possible completions. It's not Python aware, but it is of some use.

Richard> 2) Sending the toplevel definition (class or function) to the Python
Richard> buffer.

I switched to IPython to have better interaction with a spawned Python.

To use IPython you need to use the Python mode that is NOT the one from
(endorsed?) by the FSF. It gives you some completion (at least in the
*Python* buffer) and you can send pieces of the buffer to the python
process, via py-send-region (C-c |), py-execute-def-or-class (M-C-x), etc.

Richard> 3) Hints on function/method arguments. IDLE has this done nearly
Richard> right, but the hints are a bit too intrusive for me. I would like to
Richard> see them in the minibuffer.

I don't have this.

Richard> 4) (optional) I would like to see the definition of a function
Richard> function or class by hitting M-. on its name. (I understand that
Richard> this may be impossible for methods, as Emacs would have to
Richard> automagically infer the type of the object).

This is just an emacs tag file need. Have you googled for something like
emacs tags python? The issue of methods might be overcome by just moving
through tags with the same name. Yes, that requires _you_ to know when
you've hit the right thing. That's not optimal, but it's better than
nothing. Ideally you could send the definition to IPython, ask it for the
class info, and use that to jump to the right tag.

Richard> I have tried a couple of times both python-modes (the one shipped w/
Richard> Python and the one shipped w/ Emacs), pymacs and stuff like that...
Richard> And, as I said, never got it right. But, maybe I just cannot find the
Richard> way to configure it, and some configuration hints will be enough...

If you have the time, please summarize your findings. The emacs/python
world has always seemed quite amorphous to me too.

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Just for fun: Countdown numbers game solver

2008-01-20 Thread Terry Jones
Here's a solution that doesn't use any copying of lists in for recursion.
It also eliminates a bunch of trivially equivalent solutions. The countdown
function is 37 lines of non-comment code.  Sample (RPN) output below.

Terry


from operator import *

def countdown(target, nums, numsAvail, value, partialSolution, solutions, 
ops=(add, mul, sub, div)):
if not any(numsAvail):
# Ran out of available numbers. Add the solution, if we're right.
if value == target:
solutions.add(tuple(partialSolution))
elif value is None:
# Use each distinct number as a starting value.
used = set()
for i, num in enumerate(nums):
if num not in used:
numsAvail[i] = False
used.add(num)
partialSolution.append(num)
countdown(target, nums, numsAvail, num, partialSolution, 
solutions, ops)
numsAvail[i] = True
partialSolution.pop()
else:
for op in ops:
for i, num in enumerate(nums):
if numsAvail[i]:
numsAvail[i] = False
moreAvail = any(numsAvail)
try:
lastNum, lastOp = partialSolution[-2:]
except ValueError:
lastNum, lastOp = partialSolution[-1], None
# Don't allow any of:
if not any((
# Div: after mul, by 1, by 0, producing a fraction.
(op == div and (lastOp == 'mul' or num <= 1 or value % 
num != 0)),
# If initial mul/add, canonicalize to 2nd operator 
biggest.
((op == mul or op == add) and lastOp is None and num > 
lastNum),
# Don't all subtracting 0 (instead allow adding 0).
(op == sub and num == 0),
# Don't allow adding 0 unless it's the very last thing 
we do.
(op == add and num == 0 and moreAvail),
# Don't allow mult by 1 unless it's the very last thing 
we do.
(op == mul and num == 1 and moreAvail),
# Don't allow sub after add (allow add after sub).
(op == sub and lastOp == 'add'),
# If same op twice in a row, canonicalize operand order.
(lastOp == op.__name__ and num > lastNum)
)):
partialSolution.extend([num, op.__name__])
countdown(target, nums, numsAvail, op(value, num), 
partialSolution, solutions, ops)
del partialSolution[-2:]
numsAvail[i] = True

for nums, target in (((100, 9, 7, 6, 3, 1), 253),
 ((100, 9, 7, 6, 3, 1), 234),
 ((2, 3, 5), 21),
 ((7, 8, 50, 8, 1, 3), 923),
 ((8, 8), 16),
 ((8, 8, 8), 8),
 ((8, 0), 8),
 ((7,), 8),
 ((), 8),
 ((8, 8, 8, 8), 32)):
print "Target %d, numbers = %s" % (target, nums)
solutions = set()
countdown(target, nums, [True,] * len(nums), value=None, 
partialSolution=[], solutions=solutions)
for s in solutions: print '\t', s


This produces:

Target 253, numbers = (100, 9, 7, 6, 3, 1)
(7, 6, 'add', 3, 'add', 1, 'add', 9, 'mul', 100, 'add')
(7, 6, 'mul', 9, 'add', 3, 'mul', 100, 'add', 1, 'mul')
(3, 1, 'add', 6, 'mul', 7, 'sub', 9, 'mul', 100, 'add')
(100, 7, 'sub', 6, 'sub', 3, 'mul', 9, 'sub', 1, 'add')
(7, 1, 'add', 6, 'mul', 3, 'mul', 100, 'add', 9, 'add')
Target 234, numbers = (100, 9, 7, 6, 3, 1)
(6, 1, 'add', 100, 'mul', 7, 'sub', 9, 'add', 3, 'div')
(100, 1, 'sub', 3, 'div', 7, 'mul', 6, 'sub', 9, 'add')
(7, 6, 'mul', 3, 'mul', 100, 'sub', 9, 'mul', 1, 'mul')
(100, 7, 'sub', 3, 'div', 6, 'sub', 1, 'add', 9, 'mul')
(7, 6, 'mul', 3, 'mul', 1, 'sub', 100, 'add', 9, 'add')
(6, 9, 'sub', 7, 'mul', 1, 'sub', 100, 'add', 3, 'mul')
(100, 9, 'sub', 7, 'sub', 6, 'sub', 3, 'mul', 1, 'mul')
(100, 7, 'mul', 3, 'mul', 6, 'add', 9, 'div', 1, 'mul')
(100, 1, 'sub', 7, 'mul', 9, 'sub', 3, 'div', 6, 'add')
(100, 7, 'sub', 3, 'div', 1, 'sub', 9, 'add', 6, 'mul')
(7, 3, 'mul', 6, 'sub', 9, 'mul', 1, 'sub', 100, 'add')
(100, 7, 'mul', 6, 'sub', 1, 'sub', 9, 'add', 3, 'div')
(100, 9, 'add', 7, 'add', 1, 'add', 3, 'div', 6, 'mul')
(100, 9, 'sub', 7, 'div', 6, 'mul', 3, 'mul', 1, 'mul')
Target 21, numbers = (2, 3, 5)
(5, 2, 'add', 3, 'mul')
Target 923, numbers = (7, 8, 50, 8, 1, 3)
(50, 8, 'mul', 1, 'sub', 3, 'div', 7, 'mul', 8, 'sub')
Target 16, numbers = (8, 8)
(8, 8, 'add')
Target 8, numbers = (8, 8, 8)
(8, 8, 'sub', 8, 

Re: Just for fun: Countdown numbers game solver

2008-01-21 Thread Terry Jones
> "Arnaud" == Arnaud Delobelle <[EMAIL PROTECTED]> writes:
Arnaud> In countdown you are not required to use all numbers to reach the
Arnaud> target.  This means you are missing solutions, e.g.  (1, 3, 6,
Arnaud> 'mul', 'add', 7 , 'add', 9, 'mul')

Hi Arnaud.

Thanks, I didn't know that. The fix is a simple change to the first if my
function below.

WRT to the missing solution, note that my code only allowed multiplication
by 1 if it was the last thing done. That was because you can multiply by 1
at any time, and I didn't want to see those trivially equivalent solutions
(same goes for adding 0). Seeing as you're allowed to omit numbers, I've
now gotten rid of those trivial operations altogether in my solution.

Code and output below.

Terry


from operator import *

def countdown(target, nums, numsAvail, value, partialSolution, solutions, 
ops=(add, mul, sub, div)):
if value == target or not any(numsAvail):
# Ran out of available numbers. Add the solution, if we're right.
if value == target:
solutions.add(tuple(partialSolution))
elif value is None:
# Use each distinct number as a starting value.
used = set()
for i, num in enumerate(nums):
if num not in used:
numsAvail[i] = False
used.add(num)
partialSolution.append(num)
countdown(target, nums, numsAvail, num, partialSolution, 
solutions, ops)
numsAvail[i] = True
partialSolution.pop()
else:
for op in ops:
for i, num in enumerate(nums):
if numsAvail[i]:
numsAvail[i] = False
moreAvail = any(numsAvail)
try:
lastNum, lastOp = partialSolution[-2:]
except ValueError:
lastNum, lastOp = partialSolution[-1], None
# Don't allow any of:
if not any((
# Div: after mul, by 1, by 0, producing a fraction.
(op == div and (lastOp == 'mul' or num <= 1 or value % 
num != 0)),
# If initial mul/add, canonicalize to 2nd operator 
biggest.
((op == mul or op == add) and lastOp is None and num > 
lastNum),
# Don't allow add or sub of 0.
((op == add or op == sub) and num == 0),
# Don't allow mult by 1.
(op == mul and num == 1),
# Don't allow sub after add (allow add after sub).
(op == sub and lastOp == 'add'),
# If same op twice in a row, canonicalize operand order.
(lastOp == op.__name__ and num > lastNum)
)):
partialSolution.extend([num, op.__name__])
countdown(target, nums, numsAvail, op(value, num), 
partialSolution, solutions, ops)
del partialSolution[-2:]
numsAvail[i] = True

for nums, target in (((100, 9, 7, 6, 3, 1), 253),
 ((100, 9, 7, 6, 3, 1), 234),
 ((2, 3, 5), 21),
 ((7, 8, 50, 8, 1, 3), 923),
 ((8, 8), 16),
 ((8, 8, 8), 8),
 ((8, 0), 8),
 ((7,), 8),
 ((), 8),
 ((8, 8, 8, 8), 32)):
solutions = set()
countdown(target, nums, [True,] * len(nums), value=None, 
partialSolution=[], solutions=solutions)
print "%d solutions to: target %d, numbers = %s" % (len(solutions), target, 
nums)
for s in sorted(solutions, cmp=lambda a, b: cmp(len(a), len(b)) or cmp(a, 
b)):
print '\t', s


$ time countdown.py 
8 solutions to: target 253, numbers = (100, 9, 7, 6, 3, 1)
(6, 3, 'mul', 1, 'sub', 9, 'mul', 100, 'add')
(7, 6, 'mul', 9, 'add', 3, 'mul', 100, 'add')
(9, 3, 'sub', 7, 'mul', 6, 'mul', 1, 'add')
(100, 9, 'sub', 7, 'sub', 3, 'mul', 1, 'add')
(3, 1, 'add', 6, 'mul', 7, 'sub', 9, 'mul', 100, 'add')
(7, 1, 'add', 6, 'mul', 3, 'mul', 100, 'add', 9, 'add')
(7, 6, 'add', 3, 'add', 1, 'add', 9, 'mul', 100, 'add')
(100, 7, 'sub', 6, 'sub', 3, 'mul', 9, 'sub', 1, 'add')
19 solutions to: target 234, numbers = (100, 9, 7, 6, 3, 1)
(6, 3, 'mul', 7, 'add', 1, 'add', 9, 'mul')
(7, 1, 'add', 9, 'mul', 6, 'add', 3, 'mul')
(7, 3, 'mul', 1, 'sub', 6, 'add', 9, 'mul')
(7, 6, 'mul', 3, 'mul', 100, 'sub', 9, 'mul')
(100, 1, 'sub', 3, 'div', 7, 'sub', 9, 'mul')
(100, 1, 'sub', 7, 'mul', 9, 'add', 3, 'div')
(100, 7, 'mul', 3, 'mul', 6, 'add', 9, 'div')
(100, 9, 'sub', 7, 'div', 6, 'mul', 3, 'mul')
(100, 9, 'sub', 7, 'sub', 6, 'sub', 3, 'mul')
(6, 1, 'add', 100, 'mul', 7, 'sub', 9, 'add', 3, 'div')

Re: Just for fun: Countdown numbers game solver

2008-01-21 Thread Terry Jones
> "Paul" == Paul Rubin <"http://phr.cx"@NOSPAM.invalid> writes:

Hi Paul

Paul> Here's my latest, which I think is exhaustive, but it is very slow.
Paul> It prints a progress message now and then just to give the user some
Paul> sign of life.  It should print a total of 256-8 = 248 of those
Paul> messages and it takes around 20 minutes on my old 1 Ghz Pentium 3.
Paul> It does find plenty of solutions for 234, e.g.

Paul> 234 mul(9,sub(mul(mul(6,mul(3,1)),7),100))

Paul> There are some obvious clunky ways to speed it up through memoizing
Paul> and symmetry folding but I can't help thinking there's a concise
Paul> elegant way to do that too.

The symmetry folding is important, you can cut off many recursive calls. My
code runs in 0.5 seconds for the 234 problem, but I have a MacBook Pro :-)

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Just for fun: Countdown numbers game solver

2008-01-21 Thread Terry Jones
> "dg" == dg google groups <[EMAIL PROTECTED]> writes:

dg> It's great how many different sorts of solutions (or almost solutions)
dg> this puzzle has generated. Speedwise, for reference my solution posted
dg> above takes about 40 seconds on my 1.8GHz laptop, and the less elegant
dg> version (on my webpage linked to in the original post) takes about 15
dg> seconds. It seems to me like surely this problem can be more
dg> efficiently solved than that?

dg> Paul: 758 = 8+(5*((2+4)*25))

For this I get:

2 solutions to: target 758, numbers = (2, 4, 5, 8, 25)
(4, 2, 'add', 25, 'mul', 5, 'mul', 8, 'add')
(25, 4, 'mul', 5, 'sub', 8, 'mul', 2, 'sub')

real0m0.118s
user0m0.074s
sys 0m0.044s


Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Just for fun: Countdown numbers game solver

2008-01-21 Thread Terry Jones
> "cokofreedom" == cokofreedom  <[EMAIL PROTECTED]> writes:

cokofreedom> Terry, your technique is efficient and pretty readable! All
cokofreedom> that could be added now is a way to output the data in a more
cokofreedom> user-friendly print.

Yes, and a fix for the bug Arnaud just pointed out :-)
Below is code to print things more nicely for you.

Terry


from operator import *
from itertools import izip

def countdown(target, nums, numsAvail, value, partialSolution, solutions, 
ops=(add, mul, sub, div)):
if value == target or not any(numsAvail):
# Add the solution, if we're right.
if value == target:
solutions.add(tuple(partialSolution))
elif value is None:
# Use each distinct number as a starting value.
used = set()
for i, num in enumerate(nums):
if num not in used:
numsAvail[i] = False
used.add(num)
partialSolution.append(num)
countdown(target, nums, numsAvail, num, partialSolution, 
solutions, ops)
numsAvail[i] = True
partialSolution.pop()
else:
for op in ops:
for i, num in enumerate(nums):
if numsAvail[i]:
numsAvail[i] = False
moreAvail = any(numsAvail)
try:
lastNum, lastOp = partialSolution[-2:]
except ValueError:
lastNum, lastOp = partialSolution[-1], None
# Don't allow any of:
if not (
# Div: after mul, by 1, by 0, producing a fraction.
(op == div and (lastOp == 'mul' or num <= 1 or value % 
num != 0)) or
# If initial mul/add, canonicalize to 2nd operator 
biggest.
((op == mul or op == add) and lastOp is None and num > 
lastNum) or
# Don't allow add or sub of 0.
((op == add or op == sub) and num == 0) or
# Don't allow mult by 1.
(op == mul and num == 1) or
# Don't allow sub after add (allow add after sub).
(op == sub and lastOp == 'add') or
# If same op twice in a row, canonicalize operand order.
(lastOp == op.__name__ and num > lastNum)
):
partialSolution.extend([num, op.__name__])
countdown(target, nums, numsAvail, op(value, num), 
partialSolution, solutions, ops)
del partialSolution[-2:]
numsAvail[i] = True

op2sym = { 'add' : '+', 'sub' : '-', 'mul' : '*', 'div': '/', }

def pretty(s):
out = [str(s[0])]
lastOp = None
for value, op in izip(*(iter(s[1:]),) * 2):
if (op == 'mul' or op == 'div') and (lastOp == 'add' or lastOp == 
'sub'):
out.insert(0, '(')
out.append(')')
out.append(op2sym[op])
out.append(str(value))
lastOp = op
return ''.join(out)

for nums, target in (
((100, 9, 7, 6, 3, 1), 253),
((100, 9, 7, 6, 3, 1), 234),
((2, 3, 5), 21),
((7, 8, 50, 8, 1, 3), 923),
((8, 8), 16),
((8, 8, 8), 8),
((8, 0), 8),
((7,), 8),
((), 8),
((8, 8, 8, 8), 32),
((2, 4, 5, 8, 25), 758),
((2, 3, 4, 100), 406),
):
solutions = set()
countdown(target, nums, [True,] * len(nums), value=None, 
partialSolution=[], solutions=solutions)
print "%d solutions to: target %d, numbers = %s" % (len(solutions), target, 
nums)
for s in sorted(solutions, cmp=lambda a, b: cmp(len(a), len(b)) or cmp(a, 
b)):
print '\t', pretty(s)


Sample output:

8 solutions to: target 253, numbers = (100, 9, 7, 6, 3, 1)
(6*3-1)*9+100
(7*6+9)*3+100
(9-3)*7*6+1
(100-9-7)*3+1
((3+1)*6-7)*9+100
(7+1)*6*3+100+9
(7+6+3+1)*9+100
(100-7-6)*3-9+1
19 solutions to: target 234, numbers = (100, 9, 7, 6, 3, 1)
(6*3+7+1)*9
((7+1)*9+6)*3
(7*3-1+6)*9
(7*6*3-100)*9
((100-1)/3-7)*9
((100-1)*7+9)/3
(100*7*3+6)/9
(100-9)/7*6*3
(100-9-7-6)*3
((6+1)*100-7+9)/3
((6-9)*7-1+100)*3
(7*3-6)*9-1+100
7*6*3-1+100+9
(100-1)/3*7-6+9
((100-1)*7-9)/3+6
(100*7-6-1+9)/3
((100-7)/3-1+9)*6
((100-7)/3-6+1)*9
(100+9+7+1)/3*6
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Just for fun: Countdown numbers game solver

2008-01-21 Thread Terry Jones
> "Arnaud" == Arnaud Delobelle <[EMAIL PROTECTED]> writes:

Arnaud> Sorry I gave an incorrect example to illustrate my question last
Arnaud> night (I blame this on baby-induced sleep deprivation ;), so I'll
Arnaud> have another go:

Arnaud> Say I have 2, 3, 4, 100 and I want to make 406.  AFAICS there is only
Arnaud> one way: (2*3)+(4*100), i.e. in postfix notation:
Arnaud> 2 3 * 4 100 * +

Arnaud> It seemed to me that your function wouldn't generate that sort of
Arnaud> solution (as you always extend partialSolution by [num, op] making
Arnaud> the subsequence [mul, add] impossible).  Am I wrong?

No, you're right.  I actually started out with a stack solution, and then
switched to something simpler (too simple). I'm going to redo it, but maybe
not right now...

Thanks!

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Just for fun: Countdown numbers game solver

2008-01-22 Thread Terry Jones
Hi Arnaud

> I've tried a completely different approach, that I imagine as 'folding'.  I
> thought it would improve performance over my previous effort but extremely
> limited and crude benchmarking seems to indicate disappointingly comparable
> performance...

I wrote a stack-based version yesterday and it's also slow. It keeps track
of the stack computation and allows you to backtrack. I'll post it
sometime, but it's too slow for my liking and I need to put in some more
optimizations. I'm trying not to think about this problem.

What was wrong with the very fast(?) code you sent earlier?

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Just for fun: Countdown numbers game solver

2008-01-23 Thread Terry Jones
Hi Arnaud and Dan

> "Arnaud" == Arnaud Delobelle <[EMAIL PROTECTED]> writes:
>> What was wrong with the very fast(?) code you sent earlier?

Arnaud> I thought it was a bit convoluted, wanted to try something I
Arnaud> thought had more potential.  I think the problem with the second
Arnaud> one is that I repeat the same 'fold' too many times.

and later:

Arnaud> Yes, I've been doing this by writing an 'action' (see my code) that
Arnaud> takes note of all reached results.

These are both comments about pruning, if I understand you. In the first
you weren't pruning enough and in the second you're planning to prune more.

I'm giving up thinking about this problem because I've realized that the
pruning solution is fundamentally subjective. I.e., whether or not you
consider two solutions to be the "same" depends on how hard you're willing
to think about them (and argue that they're the same), or how smart you
are.

I have a new version that does some things nicely, but in trying to do
efficient pruning, I've realized that you can't satisfy everyone.

Some examples for the problem with target 253, numbers = 100, 9, 7, 6, 3, 1

Firstly, there are nice solutions that go way negative, like:

  1 7 6 3 9 sub mul mul sub   or   1 - 7 * 6 * (3 - 9)


Here's a pruning example. Are these the "same"?

  1 3 7 100 9 sub sub mul sub  or  1 - 3 * (7 - 100 - 9)
  1 3 7 9 100 sub add mul sub  or  1 - 3 * (7 - 9 - 100)

I think many people would argue that that's really the "same" and that one
of the two should not appear in the output. The same goes for your earlier
example for 406. It's 4 * 100 + 2 x 3, and 2 x 3 + 100 * 4, and so on.

My latest program does all these prunings.

But you can argue that you should push even further into eliminating things
that are the same. You could probably make a pretty fast program that
stores globally all the states it has passed through (what's on the stack,
what numbers are yet to be used, what's the proposed op) and never does
them again. But if you push that, you'll wind up saying that any two 
solutions that look like this:

  ... 1 add

e.g.

  6 9 3 sub mul 7 mul 1 add   or  6 * (9 - 3) * 7 + 1
  7 6 mul 9 3 sub mul 1 add   or  7 * 6 * (9 - 3) + 1

are the same. And if we go that far, then a really smart person might argue
that this

  100 7 sub 9 sub 3 mul 1 add  or  (100 - 7 - 9) * 3 + 1

is also the "same" (because all these solutions get to 252 and then add 1,
so they're clearly the "same", right?):


Once you've gone that far, you might even argue that on a particular
problem of a particular difficulty (subjectively matching what your brain
is capable of) all solutions that start out by pushing 1 onto the stack are
actually the "same".

And if you're even smarter than that, you might just look at any problem
and say "Hey, that's easy! The answer is X" or "There's clearly no
solution" because you'd immediately just see the answer (if any) and that
all others were just obvious variants.

E.g., if I said to you: "make 20 out of (20, 10, 10, 3)", I imagine you
could immediately list the answer(s?)

  20 = 20
  20 = 10 + 10
  20 = 20 + 10 - 10
  20 = 20 - 10 + 10

etc., and explain that those are all really the same. You'd prune on the
spot, and consider it obvious that the pruning was fully justified.

But my 6-year-old son couldn't tell you that, and I doubt he'd agree with
your prunings.

OK, enough examples.  I'm just trying to illustrate that the (difficult)
problem of efficiently pruning doesn't have an objective solution.  Pruning
is subjective. OTOH, the decision problem "does this puzzle have any
solutions or not (and if so, produce one)" does.

That's why I'm going to stop working on this. My stack solution code is
fun, but working on making it prune better is a black hole. And to make
matters worse, the more I push it (i.e., the more advanced its prunings
get), the less likely (some) people are to agree that its output is still
correct. You can't win.

If anyone wants the stack simulation code, send me an email.

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Just for fun: Countdown numbers game solver

2008-01-23 Thread Terry Jones
> "Arnaud" == Arnaud Delobelle <[EMAIL PROTECTED]> writes:

Arnaud> FWIW, I have a clear idea of what the space of solutions is, and
Arnaud> which solutions I consider to be equivalent.  I'll explain it
Arnaud> below.  I'm not saying it's the right model, but it's the one
Arnaud> within which I'm thinking.

OK. This reinforces why I'm not going to work on it anymore, the solution
is subjective (once you start pruning).

Arnaud> I think it's best to forbid negatives.  Solutions always be written
Arnaud> without any negative intermediate answer.  E.g. 1-7*6*(3-9) can be
Arnaud> done as 1+7*6*(9-3).

That's a good optimization, and I think it's easy to prove that it's
correct supposing the target is positive and the inputs are all positive.

If you consider the intermediate results in a solution, then if you ever go
negative it's because of an operation X (must be a sub or a div) and when
you next become positive it's due to an operation Y (must be add or
mul). So you can "reflect" that part of the computation by doing the
opposite operations for that formerly sub-zero intermediate sequence.

Arnaud> I don't consider these to be equivalent, because their equivalence
Arnaud> depends on understanding the meaning of subtraction and addition.

Ha - you can't have it both ways Arnaud! You don't want the computation to
go negative... doesn't that (and my "proof") have something to do with the
inverse nature of add and sub? :-)

Arnaud> (I've also applied the 'big then small' rule explained below)

And now you're taking advantage of your knowledge of > and < ...

My original code did this big then small canonicalization too.

That's my point exactly - pruning depends on who you are, how smart you
are, how hard you work, your personal taste, etc.

Arnaud> I see a solution as a full ordered binary tree.  Each node has a
Arnaud> weight (which is the result of the calculation it represents) and
Arnaud> the left child of a node has to be at least as heavy as the right
Arnaud> child.  Each parent node can be labeled + - * or /.  If a node x
Arnaud> has two children y and z and is labeled , let me write x = (y
Arnaud>  z)

Where does the sequence of numbers enter into this? You have a tree of
operations - is it acting on a stack? What's on the stack?

It sounds similar to what I've done. I walk up and down the tree, keeping
the stack and the stack history, doing operations (including pushing onto
the stack) and undoing them.

There are several more prunings I could be doing, but these require looking
further back in the stack.  E.g., I force div before mul and sub before
add, and I also apply the "big then small" rule to the intermediate stack
results if there are series of identical operations (not just to a single
operation). E.g. X / Y / Z can be re-ordered so that Y >= Z, and A + B + C
can be reordered so A >= B >= C. Doing it on the stack results is different
(but similar) to doing it on the raw input numbers.

There are lots of other little and more complex things you can do to prune.

You want to prune early, of course. The stack model of computation make
this hard because it's always legitimate to push all the numbers onto the
stack, by which point you're already deep in the tree.

And this approach only lets you do local pruning - i.e., that affect the
branch of the tree you're on. If you stored the state of the stack, the
operation you're about to do, and the (sorted) numbers remaining to be
input, then if you ever hit that configuration elsewhere in the massive
tree, you could know that you'd been that way before. But are you justified
in pruning at that point? The identical state of the computation could have
been arrived at via a very different method.

But that's where the big speed-up in this approach is.

At this realization I decided to give up :-)

Arnaud> To be perfectly honest (and expose my approach a little to your
Arnaud> argument) I added a three additional rules:

Arnaud> * Don't allow x - x
Arnaud> * Don't allow x * 1
Arnaud> * Don't allow x / 1

Yes, I do these too, including not allowing a zero intermediate (which is a
useless calculation that simply could not have been done - see, I have deep
knowledge of zero!).

Arnaud> If there are repeats in the list of starting numbers, I don't worry
Arnaud> about repeating solutions.

I handle most of those cases, but I didn't push all the way. With target 32
and input 8, 8, 8, 8 my code still gives 2 answers:

8 8 add 8 8 add add
8 8 add 8 add 8 add

>> If anyone wants the stack simulation code, send me an email.

Arnaud> I'd like to see it :)

I'll send it.

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Just for fun: Countdown numbers game solver

2008-01-23 Thread Terry Jones
> "Arnaud" == Arnaud Delobelle <[EMAIL PROTECTED]> writes:
>> 
>> Ha - you can't have it both ways Arnaud! You don't want the computation to
>> go negative... doesn't that (and my "proof") have something to do with the
>> inverse nature of add and sub? :-)

Arnaud> I think I can have it both ways, here's why: the "big then small"
Arnaud> and "no negatives" rules are applied indiscriminately by the
Arnaud> algorithm: it doesn't need to know about the history of operations
Arnaud> in order to make a decision depending on their nature.  OTOH,
Arnaud> identifying (a + b) - c and a + (b - c) for example, requires
Arnaud> inspection of the stack/tree/ calculation history.  It is an
Arnaud> *informed* decision made by the algorithm

But this is having it both ways too :-)

The reason the algorithm can do it indiscriminately is because *you* know
that it always applies, and *you* figure out and implement the algorithm
that way. The algorithm doesn't have a mind of its own, after all.

BTW, there are some very important issues here. One is about representation
(thinking about representation is one of my pet vices). When you try to
solve some real-world problem with a computer, you must choose a
representation. What many people seem to fail to recognize is that in so
doing you've already begun to solve the problem. If you pick a better
representation, your algorithm can potentially be much dumber and still be
hugely faster than a brilliant algorithm on a terrible representation.

In maintaining an A > B invariant in operations A + B and A * B, you're
using insight into the problem to influence representation. With that
better representation, your algorithm doesn't have to do so much work.

I wrote some more about this a while ago at

  
http://www.fluidinfo.com/terry/2007/03/19/why-data-information-representation-is-the-key-to-the-coming-semantic-web/

Arnaud> Note that disallowing 0 and disallowing x - x are equivalent, as
Arnaud> the only way to get your first 0 is by doing x - x.

That depends. I allow negative numbers in the input, and also 0 (I just
don't let you use it :-))

Arnaud> Thanks for the discussion, it's made me realise more clearly what I
Arnaud> was doing.

Me too! Thanks.

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Text-based data inspector for Python?

2008-01-24 Thread Terry Jones
> "kj" == kj  <[EMAIL PROTECTED]> writes:

kj> I've only recently started programming in Python, trying to wean
kj> myself from Perl.  One of the things I *really* miss from Perl is
kj> a 100% mouse-free data inspector, affectionally known as the Perl
kj> debugger, PerlDB, or just perl -d.  With it I can examine the most
kj> elaborate data structures with ease:

You actually liked the perl debugger... gasp!  OK, I used it too, but it
left a few things to be desired (the mouse was not one).

kj> And, since it's text-based, I can run it within a shell in Emacs, and
kj> transfer anything I want between it and an editing buffer without even
kj> a THOUGHT of touching the filthy mouse!  If there's a greater joy in
kj> life I have yet to find it.

I use M-x pydb to debug python from inside emacs. I like it more than the
straight pdb as it's a bit more like gdb.

In pydb (and pdb) there's p and pp to print and pretty print a python
object. They work pretty well & there's no need for the mouse.

kj> NOTE: In my address everything before the first period is backwards;
kj> and the last period, and everything after it, should be discarded.

Nice. See 
http://www.fluidinfo.com/terry/2007/10/31/stagnant-email-address-arms-race/

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Just for fun: Countdown numbers game solver

2008-01-25 Thread Terry Jones
My output looks better sorted. I.e., for s in sorted(solutions)...

Giving the easier to read/compare:

Target 234, numbers = (100, 9, 7, 6, 3, 1)
(6, 1, 'add', 100, 'mul', 7, 'sub', 9, 'add', 3, 'div')
(6, 9, 'sub', 7, 'mul', 1, 'sub', 100, 'add', 3, 'mul')
(7, 3, 'mul', 6, 'sub', 9, 'mul', 1, 'sub', 100, 'add')
(7, 6, 'mul', 3, 'mul', 1, 'sub', 100, 'add', 9, 'add')
(7, 6, 'mul', 3, 'mul', 100, 'sub', 9, 'mul', 1, 'mul')
(100, 1, 'sub', 3, 'div', 7, 'mul', 6, 'sub', 9, 'add')
(100, 1, 'sub', 7, 'mul', 9, 'sub', 3, 'div', 6, 'add')
(100, 7, 'mul', 3, 'mul', 6, 'add', 9, 'div', 1, 'mul') *
(100, 7, 'mul', 6, 'sub', 1, 'sub', 9, 'add', 3, 'div')
(100, 7, 'sub', 3, 'div', 1, 'sub', 9, 'add', 6, 'mul')
(100, 7, 'sub', 3, 'div', 6, 'sub', 1, 'add', 9, 'mul')
(100, 9, 'add', 7, 'add', 1, 'add', 3, 'div', 6, 'mul')
(100, 9, 'sub', 7, 'div', 6, 'mul', 3, 'mul', 1, 'mul')
(100, 9, 'sub', 7, 'sub', 6, 'sub', 3, 'mul', 1, 'mul')

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: type, object hierarchy?

2008-02-03 Thread Terry Jones
> "Arnaud" == Arnaud Delobelle <[EMAIL PROTECTED]> writes:

Arnaud> Are you suggesting a new axiom for propositional logic:
Arnaud> ((P => Q) ^ (R => Q)) => (P => R)  ?

Arnaud> I.e. in fruit logic: every orange is a fruit and every apple is a
Arnaud> fruit, therefore every orange is an apple.

This is otherwise known as Winterson's law:

  1. Every orange is a fruit
  2. Every apple is a fruit

  => Oranges are not the only fruit

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: flattening a dict

2008-02-17 Thread Terry Jones
Hi Arnaud & Benjamin

Here's a version that's a bit more general. It handles keys whose values
are empty dicts (assigning None to the value in the result), and also dict
keys that are not strings (see the test data below). It's also less
recursive as it only calls itself on values that are dicts.

But it returns a dict whose keys are tuples. You then need to decide
what to do with this. Hence the helper function strdictflatten for the case
when all dict keys can be converted to str.

As I told Arnaud in email, I greatly prefer his version for its elegance.

Terry


def dictflatten(d, prefix=None):
result = {}
if prefix is None: prefix = tuple()
for k, v in d.iteritems():
key = prefix + (k,)
if isinstance(v, dict):
if v:
result.update(dictflatten(v, key))
else:
result[key] = None
else:
result[key] = v
return result

def strdictflatten(d, sep='/'):
return dict((sep.join(map(str, k)), v) for k, v in 
dictflatten(d).iteritems())

if __name__ == '__main__':
test = {
"" : {},
4 : 5,
"mays" : {"eggs" : "spam"},
"jam" : {"soda" : {"love" : "dump"}},
"lamba" : 23
}

d = dictflatten(test)
print strdictflatten(test)

 {'': None, 'lamba': 23, 'mays/eggs': 'spam', '4': 5, 'jam/soda/love': 
 'dump'}
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: flattening a dict

2008-02-17 Thread Terry Jones
> "Arnaud" == Arnaud Delobelle <[EMAIL PROTECTED]> writes:

Arnaud> BTW, I keep using the idiom itertools.chain(*iterable).  I guess
Arnaud> that during function calls *iterable gets expanded to a tuple.
Arnaud> Wouldn't it be nice to have an equivalent one-argument function
Arnaud> that takes an iterable of iterables and return the 'flattened'
Arnaud> iterable?

This reminds me of a function I wrote a while back that iterates over all
its arguments, even calling passed functions and iterating over their
results. I knew *even less* about Python then than I do now; please set
flamethrowers to "Constructive Criticism".

  http://www.fluidinfo.com/terry/2007/05/07/iteranything/

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: European python developers

2008-02-26 Thread Terry Jones
Hi Jerry

> I am hoping one or two members of this list might help me locate in Europe

Do you mean you want to re-locate here or that you're trying to locate
(i.e., find) people already here?

> My personal first choice is Spain

I'm in Barcelona (but not looking for work!).  There are a couple of
companies around that I know of who use Python and quite a lot of
individual programmers.

> The point is that I can't really afford to run around and experiment, so I
> was hoping for some helpful comments or suggestions on how to research

I'm not sure what you're wanting to research. See first comment above.

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Question on using distutils.core.run_setup

2008-03-17 Thread Terry Jones
I'm trying to programmatically install something built using distutils. I
found distutils.core.run_setup and can use it via

  >>> dist = run_setup('setup.py', ['-q', 'install'])

Is that the recommended way to do an install from inside Python (as opposed
to doing it on the command line)?

If so, how can I find where the thing(s) I installed now resides?  I saw
dist.packages but that just has top-level package names. I could __import__
these (and then use module.__file__), but that's not a good solution as it
may run code I don't want run. On my machine, I can see the packages have
been installed under the system's python2.5/site-packages directory. But
how can I determine that programmatically? I don't see anything useful on
the distutils.dist.Distribution instance I'm getting back from run_setup.

Thanks!

Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Global variables in modules...

2008-03-28 Thread Terry Jones
> "Peter" == Peter Otten <[EMAIL PROTECTED]> writes:
Peter> [EMAIL PROTECTED] wrote:
[snip]
>> ...can someone explain why invoking a.py prints 0?
>> I would have thought that the global variable 'g' of module 'a' would
>> be set to 1...

Peter> When you run a.py as a script it is put into the sys.modules module
Peter> cache under the key "__main__" instead of "a". Thus, when you import
Peter> a the cache lookup fails and a.py is executed again. You end up with
Peter> two distinct copies of the script and its globals
[snip]


Suggesting the following horribleness in a.py

g = 0

def main():
global g
import b
g = 1
b.callb()

if __name__ == "__main__":
import sys, __main__
sys.modules['a'] = __main__
main()


Terry
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: [Twisted-Python] Counting errors in a DeferredList and avoiding Unhandled error in Deferred messages

2008-05-05 Thread Terry Jones
Hi Jean-Paul

> You can do this (if you replace `pass´ with `None´, anyway) or you can pass
> `consumeErrors=True´ to the `DeferredList´ initializer which will make it
> do something equivalent.

Thanks. Sorry - I should have just read the docs again :-)

Terry
--
http://mail.python.org/mailman/listinfo/python-list