Re: Python code written in 1998, how to improve/change it?

2006-01-28 Thread Petr Jakes
To provide some feedback I vould like to show the code which works fine
for me as a FSM machine. As speed is not the crucial issue for my
application, I have decided to use  an approach showed in the Skip
Montanaro's code.
http://orca.mojam.com/~skip/python/fsm.py
(it is using dictionary to store state/transition dependencies).

Tanks to all for your previous postings.

Petr Jakes

State machine example, for the pull/push button mentioned earlier in
this discussion thread

class FSM:
'''the states dictionary contains user defined state/transition
table in the format:
{'start_state': {'event': {'guard': ('action', 'end_state')}},
according to the actual start_state, event and guard
combination,
the execute method reads the relevant  action and end_state
from the states dictionary, then executes action and setup
end_state   '''
def __init__(self):
self.states = {}

def add(self,start_state, event_trigger,event_guard,
action,newstate):
add a new state/transition information to the state
machine dictionary
if self.states.has_key(start_state)== False :
self.states[start_state]={}
if self.states[start_state].has_key(event_trigger)== False :
self.states[start_state][event_trigger]={}
if
self.states[start_state][event_trigger].has_key(event_guard)== False :
self.states[start_state][event_trigger][event_guard]={}
self.states[start_state][event_trigger][event_guard]=(action,
newstate)

def start(self, state):
set the start state
self.state = state

def execute(self, event,guard=None):
'''according to the actual start_state, event and guard
combination
read the relevant  action and end_state from the
states dictionary,
then execute action and setup end_state '''
action, end_state = self.states[self.state][event][guard]
if action is not None:
apply(action, (self.state, event))
self.state = end_state
return

'''actions they has to be executed while the event occurs'''
def motor_off(state, input): print pushing the switch to the OFF
position
def motor_on(state, input): print lifting the switch to the ON
position


fsm = FSM()

'''we have to define state/transition table first,
wher state transitions are defined as:
('start_state', 'event', 'guard', 'action', 'end_state)'''
fsm.add(ON,lift,None,None,ON)
fsm.add(ON,push,None,motor_off,OFF)
fsm.add(OFF,push,None,None,OFF)
fsm.add(OFF,lift,None,motor_on,ON)

fsm.start(ON)
print start state is, fsm.state
events=(push,push,push,lift,lift,push,lift,push,lift,lift,lift,push,lift)

for  event in (events):

fsm.execute(event)
print switch is , fsm.state

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


Re: Python code written in 1998, how to improve/change it?

2006-01-26 Thread Bengt Richter
On Wed, 25 Jan 2006 15:50:27 -0600, [EMAIL PROTECTED] wrote:

 
 If they need to resume their calculations from where they left off
 after the last yield.

Wolfgang Well, no, independently from that.

Wolfgang Just to avoid to inital overhead of the function call.

How do you pass in parameters?  Consider:

def square(x):
return x*x

vs

def square(x)
while True:
yield x*x

How do you get another value of x into the generator?

 def square(x):
...   while True:
... yield x*x
...
 g = square(2)
 g
generator object at 0x3b9d28
 g.next()
4
 g.next()
4
 g.next(3)
Traceback (most recent call last):
  File stdin, line 1, in module
TypeError: expected 0 arguments, got 1

  def square(xbox):
 ... while True: yield xbox[0]*xbox[0]
 ...
  xbox = [3]
  g = square(xbox)
  g.next()
 9
  xbox[0]=4
  g.next()
 16
  [g.next() for xbox[0] in xrange(10)]
 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

One way to answer your question literally,
whatever it may do to your gag reflex ;-)

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


Re: Python code written in 1998, how to improve/change it?

2006-01-25 Thread Bengt Richter
On Tue, 24 Jan 2006 14:58:27 -0600, [EMAIL PROTECTED] wrote:


Wolfgang So basically if I want to write a long-running program in
Wolfgang Python, it would make sense to code all functions that are
Wolfgang likely to be called more than once as generators...

If they need to resume their calculations from where they left off after the
last yield.
Hm, I wonder how (all untested)

def foo(x,y):
return x**2+y**2
for pair in pairs: print foo(*pair)

would compare to

def bar(arglist):
while True:
x,y = arglist
yield x**2 + y**2
barargs = []
ibar = bar(barargs).next
for barargs[:] in pairs: print ibar()

Of course, for simple stuff there's always manual inlining ;-)

for x, y in pairs: print x**2, y**2

Hm, bf warning it might be interesting if one could bind arg list items of
a generator from the outside, e.g.,

def gfoo(x, y):
while True:
   yield x**2 + y**2

ifoo = gfoo('dummy','dummy') # or first pair
for ifoo.x, ifoo.y in pairs: print ifoo.next()

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


Re: Python code written in 1998, how to improve/change it?

2006-01-25 Thread Wolfgang Keller
  So basically if I want to write a long-running program in
  Python, it would make sense to code all functions that are
  likely to be called more than once as generators...

This was meant as a question.

 If they need to resume their calculations from where they left off after the
 last yield.

Well, no, independently from that.

Just to avoid to inital overhead of the function call.

?

Sincerely,

Wolfgang Keller

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


Re: Python code written in 1998, how to improve/change it?

2006-01-25 Thread Fredrik Lundh
Wolfgang Keller wrote:

   So basically if I want to write a long-running program in
   Python, it would make sense to code all functions that are
   likely to be called more than once as generators...

 This was meant as a question.

  If they need to resume their calculations from where they left off after the
  last yield.

 Well, no, independently from that.

 Just to avoid to inital overhead of the function call.

 ?

what makes you think that resuming a generator won't involve function
calls ?

/F



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


Re: Python code written in 1998, how to improve/change it?

2006-01-25 Thread Wolfgang Keller
 what makes you think that resuming a generator won't involve function
 calls ?

That was not what I wrote.

I referred to what Peter Hansen [EMAIL PROTECTED] wrote in 
[EMAIL PROTECTED]:

 I believe the more modern approach to this is to use generators in some 
 way, yield each other as the next state.  This way you avoid all almost 
 all the function call overhead (the part that takes significant time, 
 which is setting up the stack frame)

The way I understand this, resuming a generator causes less overhead than the 
inital overhead of a function call.

Again, I'm just a poor scripting dilettant who's asking questions.

Sincerely,

Wolfgang Keller

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


Re: Python code written in 1998, how to improve/change it?

2006-01-25 Thread Magnus Lycka
Wolfgang Keller wrote:
 The way I understand this, resuming a generator causes less overhead than the 
 inital overhead of a function call.

I don't have Python 2.4 handy, but it doesn't seem to be true in 2.3.
I'm not very proficient with generators though, so maybe I'm doing
something stupid here...

  from __future__ import generators
  def f():
... return 1
...
  def g():
... while 1:
... yield 1
...
  it = g()
  import time
  def t(c, n):
... start = time.time()
... for i in xrange(n):
... c()
... print time.time()-start
...
  t(f,100)
0.277699947357
  t(f,100)
0.279093980789
  t(f,100)
0.270813941956
  t(it.next,100)
0.297060966492
  t(it.next,100)
0.263942956924
  t(it.next,100)
0.293347120285

For refernce:
  def t0(c, n):
... start = time.time()
... for i in xrange(n):
... pass
... print time.time()-start
...
  t0(it.next,100)
0.0523891448975

Maybe the ratio is completely different in a newer Python than
2.3.4 (RH EL3 standard install). Or maybe it's very different if
there are plenty of local variables etc in f / g.

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


Re: Python code written in 1998, how to improve/change it?

2006-01-25 Thread Peter Hansen
Wolfgang Keller wrote:
what makes you think that resuming a generator won't involve function
calls ?
 
 That was not what I wrote.
 
 I referred to what Peter Hansen [EMAIL PROTECTED] wrote in 
 [EMAIL PROTECTED]:
 
I believe the more modern approach to this is to use generators in some 
way, yield each other as the next state.  This way you avoid all almost 
all the function call overhead (the part that takes significant time, 
which is setting up the stack frame)
 
 The way I understand this, resuming a generator causes less overhead than the 
 inital overhead of a function call.

I think in the spirit of avoiding premature optimization and all 
things related, now is the time to re-inject the other part of my 
above-quoted posting, where I also said:


Of course, if you have a state machine with many small states each doing
a tiny bit of processing and you're still concerned over performance,
you probably should be looking into Pysco or Pyrex and avoid making your
code really unreadable.


-Peter

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


Re: Python code written in 1998, how to improve/change it?

2006-01-25 Thread skip

Wolfgang So basically if I want to write a long-running program in
Wolfgang Python, it would make sense to code all functions that are
Wolfgang likely to be called more than once as generators...

Skip If they need to resume their calculations from where they left off
Skip after the last yield.

Bengt Hm, I wonder how (all untested)
...
Bengt would compare to
...

I was thinking about things like complex tokenizers.  Take a look, for
example, at the SpamBayes tokenizer and think about how to implement it
without yield.  Clearly it can be don (and not all that difficult).  Still,
I think the generator version would be easier to understand and modify.

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


Re: Python code written in 1998, how to improve/change it?

2006-01-25 Thread skip
 
 If they need to resume their calculations from where they left off
 after the last yield.

Wolfgang Well, no, independently from that.

Wolfgang Just to avoid to inital overhead of the function call.

How do you pass in parameters?  Consider:

def square(x):
return x*x

vs

def square(x)
while True:
yield x*x

How do you get another value of x into the generator?

 def square(x):
...   while True:
... yield x*x
...
 g = square(2)
 g
generator object at 0x3b9d28
 g.next()
4
 g.next()
4
 g.next(3)
Traceback (most recent call last):
  File stdin, line 1, in module
TypeError: expected 0 arguments, got 1

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


Re: Python code written in 1998, how to improve/change it?

2006-01-24 Thread Wolfgang Keller
On Fri, 20 Jan 2006 05:16:57 +0100, Peter Hansen wrote
(in article [EMAIL PROTECTED]):

 I believe the more modern approach to this is to use generators in some 
 way, yield each other as the next state.  This way you avoid all almost 
 all the function call overhead (the part that takes significant time, 
 which is setting up the stack frame) and don't have to resort to 
 bytecode hacks for better performance.

Dumb question from a endless newbie scripting dilettant:

Do you have a reference to a cookbook example for this method?

Sidequestion: As I understand it, as a general rule generators are more 
efficient than functions in any case where the function is called several 
times, right? So basically if I want to write a long-running program in 
Python, it would make sense to code all functions that are likely to be 
called more than once as generators...

TIA,

Sincerely,

Wolfgang Keller

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


Weird generator id() behaviour (was Re: Python code written in 1998, how to improve/change it?)

2006-01-24 Thread Carl Cerecke
Wolfgang Keller wrote:
 On Fri, 20 Jan 2006 05:16:57 +0100, Peter Hansen wrote
 (in article [EMAIL PROTECTED]):
 
 
I believe the more modern approach to this is to use generators in some 
way, yield each other as the next state.  This way you avoid all almost 
all the function call overhead (the part that takes significant time, 
which is setting up the stack frame) and don't have to resort to 
bytecode hacks for better performance.
 
 
 Dumb question from a endless newbie scripting dilettant:
 
 Do you have a reference to a cookbook example for this method?

It turns out that generators are more efficient than the eval function 
excuting bits of compiled code. About 20-25% faster.

However, the generators exhibit some weird behaviour.

The included code shows an example using a compile/eval FSM method, and 
a generator method. Note, in particular, the pattern of ids of the 
generator objects. I had expected some complications in the ids, but not 
a repeatable sequence of length 3 after the first generator object.

What is going on? Anybody?

#!/usr/bin/env python

import time

s_on = compile('''
print 'on'
action = next_action()
if action == 'lift':
 state = s_on
elif action == 'push':
 state = s_off
else:
 state = None
''','','exec')

s_off = compile('''
print 'off'
action = next_action()
if action == 'lift':
 state = s_on
elif action == 'push':
 state = s_off
else:
 state = None
''','','exec')


def g_on():

 print on
 action = next_action()
 if action == 'lift':
 yield g_on()
 elif action == 'push':
 yield g_off()
 else:
 yield None

def g_off():

 print off
 action = next_action()
 if action == 'lift':
 yield g_on()
 elif action == 'push':
 yield g_off()
 else:
 yield None


def actions(n):
 import random
 for i in range(n-1):
 yield random.choice(['lift','push'])
 yield None

#r = 100
r = 10
next_action = actions(r).next

#a = time.clock()
while next_action():
 pass
#z = time.clock()
#print action generator:,z-a
next_action = actions(r).next
#print ---
state = s_on # start state
#a = time.clock()
while state:
 eval(state)
#z = time.clock()
#print z-a
print ---

next_action =  actions(r).next
s_g_on = g_on()
s_g_off = g_off()
state = s_g_on
#a = time.clock()
while state:
 print id(state)
 state = state.next()
#z = time.clock()
#print z-a


#Cheers,
#Carl.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Python code written in 1998, how to improve/change it?

2006-01-24 Thread skip

Wolfgang So basically if I want to write a long-running program in
Wolfgang Python, it would make sense to code all functions that are
Wolfgang likely to be called more than once as generators...

If they need to resume their calculations from where they left off after the
last yield.

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


Re: Weird generator id() behaviour (was Re: Python code written in 1998, how to improve/change it?)

2006-01-24 Thread Wolfgang Keller
On Tue, 24 Jan 2006 21:19:26 +0100, Carl Cerecke wrote
(in article [EMAIL PROTECTED]):

 def g_on():
 
  print on
  action = next_action()
  if action == 'lift':
  yield g_on()
  elif action == 'push':
  yield g_off()
  else:
  yield None
 
 def g_off():
 
  print off
  action = next_action()
  if action == 'lift':
  yield g_on()
  elif action == 'push':
  yield g_off()
  else:
  yield None

Amazing.

Executable pseudo-code, really. :-)

And that's even (run-time) efficient?

Tanks a lot,

Sincerely,

Wolfgang Keller

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


Re: Weird generator id() behaviour (was Re: Python code written in 1998, how to improve/change it?)

2006-01-24 Thread Fredrik Lundh
Carl Cerecke wrote:

 It turns out that generators are more efficient than the eval function
 excuting bits of compiled code. About 20-25% faster.

why are you using generators to return things from a function, when
you can just return the things ?

def f_on():
print on
action = next_action()
if action == 'lift':
return f_on
elif action == 'push':
return f_off

def f_off():
print off
action = next_action()
if action == 'lift':
return f_on
elif action == 'push':
return f_off

state = f_on
while state:
state = state()

/F



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


Re: Python code written in 1998, how to improve/change it?

2006-01-23 Thread Bengt Richter
On Mon, 23 Jan 2006 08:53:59 +1300, Carl Cerecke [EMAIL PROTECTED] wrote:

Bengt Richter wrote:
 On Thu, 19 Jan 2006 23:16:57 -0500, Peter Hansen [EMAIL PROTECTED] wrote:

 How about something like
 
   actions = dict(
  ...a=compile('print A; state=b','','exec'),
  ...b=compile('print B; state=c','','exec'),
  ...c=compile('print C; state=None','','exec')
  ... )
   state = 'a'
   while state: eval(actions[state])
  ...
  A
  B
  C

Good idea. But we can eliminate the dictionary lookup:

a1 = compile('print A; state=b1','','exec')
b1 = compile('print B; state=c1','','exec')
c1 = compile('print C; state=None','','exec')

state = a1
while state:
 eval(state)

Cool. But unfortunately, neither version works inside a function's local 
namespace.
Using exec instead of eval seems to do it in either context though.
Now, how can we get optimized code (i.e., LOAD_FAST vs LOAD_NAME etc in a1 etc)
without byte code hackery?

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


Re: Python code written in 1998, how to improve/change it?

2006-01-22 Thread Carl Cerecke
Bengt Richter wrote:
 On Thu, 19 Jan 2006 23:16:57 -0500, Peter Hansen [EMAIL PROTECTED] wrote:

 How about something like
 
   actions = dict(
  ...a=compile('print A; state=b','','exec'),
  ...b=compile('print B; state=c','','exec'),
  ...c=compile('print C; state=None','','exec')
  ... )
   state = 'a'
   while state: eval(actions[state])
  ...
  A
  B
  C

Good idea. But we can eliminate the dictionary lookup:

a1 = compile('print A; state=b1','','exec')
b1 = compile('print B; state=c1','','exec')
c1 = compile('print C; state=None','','exec')

state = a1
while state:
 eval(state)


Cheers,
Carl
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Python code written in 1998, how to improve/change it?

2006-01-22 Thread Petr Jakes
Sorry, I can't get in. Can you please show me, how to use your approach
on the simple push/push ON/OFF button for example please?

PS: seriously it is not a homework :) and I feel it like a shame I am
asking such a simple questions :(

States: ON, OFF
Transition event: push, lift

transition diagram:
=

   ___ lift
  |   |
 _V___|__
 ,-|  ON|__
 |  ||  |
lift |  | push
 |      |
 '--|  OFF   |-'
||
  ^   |
  |___|push

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


Re: Python code written in 1998, how to improve/change it?

2006-01-22 Thread Petr Jakes
Sorry for the typo in my previous posting. Of course it has to be: 
simple liftt/push ON/OFF button

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


Re: Python code written in 1998, how to improve/change it?

2006-01-22 Thread Carl Cerecke
Petr Jakes wrote:
 Sorry, I can't get in. Can you please show me, how to use your approach
 on the simple push/push ON/OFF button for example please?
 
 PS: seriously it is not a homework :) and I feel it like a shame I am
 asking such a simple questions :(
 
 States: ON, OFF
 Transition event: push, lift
 
 transition diagram:
 =
 
___ lift
   |   |
  _V___|__
  ,-|  ON|__
  |  ||  |
 lift |  | push
  |      |
  '--|  OFF   |-'
 ||
   ^   |
   |___|push
 

As a starting point, how about:

l = 'lift'
p = 'push'

action_sequence = [l,p,p,l,l,p,l,p,None]
next_action = iter(action_sequence).next

s_on = compile('''
print 'on'
action = next_action()
if action == 'lift':
 state = s_on
elif action == 'push':
 state = s_off
else:
 state = None
''','','exec')

s_off = compile('''
print 'off'
action = next_action()
if action == 'lift':
 state = s_on
elif action == 'push':
 state = s_off
else:
 state = None
''','','exec')


state = s_on # start state
while state:
 eval(state)



Cheers,
Carl
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Python code written in 1998, how to improve/change it?

2006-01-20 Thread Bengt Richter
On Thu, 19 Jan 2006 23:16:57 -0500, Peter Hansen [EMAIL PROTECTED] wrote:

Carl Cerecke wrote:
 Carl Cerecke wrote:
Ah. Well, my post suggested, as one option, the callables call
each other directly.
 
 Doh! No I didn't. And they shouldn't. Otherwise the call stack
 gets out of hand. But I did suggest that each callable representing a 
 state set a global variable, just before it returns, to the callable 
 representing the next state to be called. Still no map required. Just a 
 while loop. In any case, the function call/return is wasted cycles.

I believe the more modern approach to this is to use generators in some 
way, yield each other as the next state.  This way you avoid all almost 
all the function call overhead (the part that takes significant time, 
which is setting up the stack frame) and don't have to resort to 
bytecode hacks for better performance.

Of course, if you have a state machine with many small states each doing 
a tiny bit of processing and you're still concerned over performance, 
you probably should be looking into Pysco or Pyrex and avoid making your 
code really unreadable.

How about something like

  actions = dict(
 ...a=compile('print A; state=b','','exec'),
 ...b=compile('print B; state=c','','exec'),
 ...c=compile('print C; state=None','','exec')
 ... )
  state = 'a'
  while state: eval(actions[state])
 ...
 A
 B
 C

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


Re: Python code written in 1998, how to improve/change it?

2006-01-20 Thread Steven D'Aprano
On Fri, 20 Jan 2006 10:27:58 +1300, Carl Cerecke wrote:

 We want a goto.
 
 Unfortunately, this is about the only problem I can think of where gotos 
 are useful. And for reasons well explained elsewhere, gotos are Not Good.
 
 I've solved this in a very efficient, but rather unpythonic way.

There is a module that does both GOTO and COMEFROM.

http://www.entrian.com/goto/

PLEASE don't use it in real code. 



-- 
Steven.

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


Re: Python code written in 1998, how to improve/change it?

2006-01-19 Thread Steve Holden
Petr Jakes wrote:
 Hello,
 I am trying to study/understand OOP principles using Python. I have
 found following code http://tinyurl.com/a4zkn about FSM (finite state
 machine) on this list, which looks quite useful for my purposes. As
 this code was posted long time ago (November 1998) I would like to ask
 if the principles used in this code are still valid in the modern
 Python and if/how it can be improved (revrited) using futures of
 current version of Python.
 Thanks for your comments for a newbie and for your patience.

Python places great store on backwards-compatibility. Consequently I'd 
suggest you try the code out, and report any problems you find back on 
this list. I'd give you odds it will work.

regards
  Steve
-- 
Steve Holden   +44 150 684 7255  +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006  www.python.org/pycon/

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


Re: Python code written in 1998, how to improve/change it?

2006-01-19 Thread Carl Cerecke
Petr Jakes wrote:
 Hello,
 I am trying to study/understand OOP principles using Python. I have
 found following code http://tinyurl.com/a4zkn about FSM (finite state
 machine) on this list, which looks quite useful for my purposes. As
 this code was posted long time ago (November 1998) I would like to ask
 if the principles used in this code are still valid in the modern
 Python and if/how it can be improved (revrited) using futures of
 current version of Python.
 Thanks for your comments for a newbie and for your patience.
 Petr Jakes
 

Python has no goto.

If you think about the possible execution paths through a function, you 
can represent that as a finite state machine: A FSM is just a graph, and 
in a function, you get a FSM by treating each statement as a node, and 
statements that can follow that statement (in the execution order) have 
a (directed) edge to them (let's ignore exceptions for the moment). So for:

a=b
if a == 3:
  z = 0
else:
  z = 1
print 'spam'

we get:
(a=b) - (if a == 3) - (z = 0)
||
 (z = 1) - (print 'spam)

Now, when we want to emulate a FSM in a function directly (rather than 
some slower table-driven scheme), we need to use the constructs provided 
by the language. But simple 'if' statements just don't cut it. We end up 
with:

while state != 'final':
   if state == 1:
 # do state 1 stuff here
 state = 3
   elif state == 2:
 # if cond:
   state == 1
 else:
   state == 99
   elif
 ...
   else:
 # unknown state

Problem with this approach is that, on average, you'll have n/2 
comparisons of the state variable. What you want is to jump directly 
from state 1 to state 3 without having to go anywhere else. You want a goto.

Another goto-less way is with functions. Each function is a state, which 
sets a global function variable to the next state-function:

def f_state_1():
   global func
   # do state 1
   func = f_state_3

def f_state_2():
   global func
   # do state_2 stuff
   if cond:
 func = f_state_1
   else:
 func = f_state_99

#etc.

func = f_state_1 # start_state

while func != None:
   func()


We've eliminated the numerous comparisons, and it is, arguably, more 
pythonic. But now you have a function-call overhead on each state 
transition, and any information shared between states has to be in an 
enclosing scope.

We want a goto.

Unfortunately, this is about the only problem I can think of where gotos 
are useful. And for reasons well explained elsewhere, gotos are Not Good.

I've solved this in a very efficient, but rather unpythonic way.

First, you write (or generate) the code in the first way indicated 
above. Lots of inefficient 'if' statements in one big function (say, 
'fsm'). Then, you write another function (say 'gotoize') which takes fsm 
as an argument, and *changes the byte code* of the fsm code object to 
add JUMP_ABSOLUTE bytecodes (i.e. gotos) where the 'state' variable gets 
reassigned.
fsm can then be decorated with gotoize, and you have a very fast (for 
python) FSM. You are, essentially, doing some (fairly simple) byte-code 
optimisation.

I can dig out the code if you are interested (except it's pretty untidy 
at the moment. I'd be ashamed to show it in it's current state :-)

Ooops. I just reread your post and you claim (or is that plead?) 
newbie status. Sorry if this is over your head. Hope it helps anyway.

Cheers,
Carl.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Python code written in 1998, how to improve/change it?

2006-01-19 Thread Dan Sommers
On Fri, 20 Jan 2006 10:27:58 +1300,
Carl Cerecke [EMAIL PROTECTED] wrote:

 Petr Jakes wrote:

[ a query regarding some 1998 python code that implements a finite state
machine ]

 Python has no goto.

Thank goodness!

 func = f_state_1 # start_state

 while func != None:
func()

 We've eliminated the numerous comparisons, and it is, arguably, more
 pythonic ...

Agreed.

 ... now you have a function-call overhead on each state transition ...

Have you profiled your code and demonstrated that this particular
function call consumes too much time?

If you can't stand the overhead of one [extra, parameterless] function
call per transition, especially *after* having eliminated the numerous
comparisons, then Python may not be the best tool for the job.

 ... and any information shared between states has to be in an
 enclosing scope.

Well, no, not exactly.  Consider an instance of a class that contains
its own data (and perhaps some of its own transition functions) but
inherits the basic state machine machinery from a finite state machine
class (or simply passes itself to a function that implements a finite
state machine by examining attributes of its argument).

And then there are (or should be!) purists who will claim that if your
state machine requires information to be shared between states, then you
don't have enough states!  ;-)

 We want a goto.

No, we don't.

Regards,
Dan

-- 
Dan Sommers
http://www.tombstonezero.net/dan/
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Python code written in 1998, how to improve/change it?

2006-01-19 Thread Dave Hansen
On Fri, 20 Jan 2006 10:27:58 +1300 in comp.lang.python, Carl Cerecke
[EMAIL PROTECTED] wrote:

[...]

Python has no goto.

+1

[...]

We want a goto.

-1

Regards,
-=Dave

-- 
Change is inevitable, progress is not.
-- 
http://mail.python.org/mailman/listinfo/python-list


Goto in python - NO! (was Re: Python code written in 1998, how to improve/change it?)

2006-01-19 Thread Carl Cerecke
Dave Hansen wrote:
 On Fri, 20 Jan 2006 10:27:58 +1300 in comp.lang.python, Carl Cerecke
 [EMAIL PROTECTED] wrote:
 
 [...]
 
Python has no goto.
 
 
 +1
 
 [...]
 
We want a goto.
 
 
 -1

I agree entirely. My (rather unclearly made) point was that, for the 
particular application (FSM), an unrestricted jump is really handy. Not 
that we want goto in python (Eurg!!) just to make this one case easier.

I learnt programming on a Commodore-64 where BASIC programs where 
required to be liberally sprinkled with gotos (No else. Only one, 
global, scope. No real functions). I know first hand the pain gotos can 
inflict. We don't want to go back there.

But. They are still really handy for directly implementing a FSM in 
code! Hence my JUMP_ABSOLUTE bytecode rewrite hack for FSMs.

Cheers,
Carl.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Python code written in 1998, how to improve/change it?

2006-01-19 Thread Carl Cerecke
Dan Sommers wrote:
 On Fri, 20 Jan 2006 10:27:58 +1300,
 Carl Cerecke [EMAIL PROTECTED] wrote:

... now you have a function-call overhead on each state transition ...
 
 
 Have you profiled your code and demonstrated that this particular
 function call consumes too much time?

Yes. For a parser reading a reasonable size input file, the difference 
is large enough to be noticeable.

 If you can't stand the overhead of one [extra, parameterless] function
 call per transition, especially *after* having eliminated the numerous
 comparisons, then Python may not be the best tool for the job.

Maybe. Except the bytecode *is* quite fast enough. There is just no way 
of writing python code so the compiler can generate that bytecode. Hence 
the JUMP_ABSOLUTE bytecode rewrite hack.

Cheers,
Carl.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Python code written in 1998, how to improve/change it?

2006-01-19 Thread Petr Jakes
Thanks for your comments,
The mentioned 8 years old code actually works somehow.

I am trying to solve very similar problem about FSM as the code in the
example does and I do not want to be overburden by the if/elif stuff or
by declaring state functions (which IMHO is very similar approach as
if/elif).

Because of above mentioned, something like FSM-generator looks as a way
to go for me (if I can judge with my poor skills).

I am using Steve's book Python Web Programming which is actually the
best I have found about OOP, classes etc. but as a newbie I am just
lost with subclass and mapping attributes etc. while trying to study
the code in the example (http://tinyurl.com/a4zkn).

All I wanted to know, if, thanks to the improvements in the Python
functionality over the years, it is possible to simplify somhow the old
code.

Otherwise I have to dig through :)

Petr Jakes

PS:
I agree and I do understand the reasons why NOT to use GOTO statements
in the code (aka spaghetti code).

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


Re: Python code written in 1998, how to improve/change it?

2006-01-19 Thread Chris Mellon
On 19 Jan 2006 15:53:54 -0800, Petr Jakes [EMAIL PROTECTED] wrote:
 Thanks for your comments,
 The mentioned 8 years old code actually works somehow.

 I am trying to solve very similar problem about FSM as the code in the
 example does and I do not want to be overburden by the if/elif stuff or
 by declaring state functions (which IMHO is very similar approach as
 if/elif).


I'm not sure why nobody else in this thread said it, but the most
common way of implementing state machines I've seen in Python (unless
theres only a couple states you can manage with if/elif) is to use a
dict to map states to callables.



 Because of above mentioned, something like FSM-generator looks as a way
 to go for me (if I can judge with my poor skills).

 I am using Steve's book Python Web Programming which is actually the
 best I have found about OOP, classes etc. but as a newbie I am just
 lost with subclass and mapping attributes etc. while trying to study
 the code in the example (http://tinyurl.com/a4zkn).

 All I wanted to know, if, thanks to the improvements in the Python
 functionality over the years, it is possible to simplify somhow the old
 code.

 Otherwise I have to dig through :)

 Petr Jakes

 PS:
 I agree and I do understand the reasons why NOT to use GOTO statements
 in the code (aka spaghetti code).

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

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


Re: Python code written in 1998, how to improve/change it?

2006-01-19 Thread Carl Cerecke
Chris Mellon wrote:

 I'm not sure why nobody else in this thread said it, but the most
 common way of implementing state machines I've seen in Python (unless
 theres only a couple states you can manage with if/elif) is to use a
 dict to map states to callables.

Ah. Well, my post suggested, as one option, the callables call
each other directly. No mapping required. But if you want a dict
in there somewhere, feel free to put one in :-)

I was complaining about the function-call overhead of this method
though, which can be quite significant for a FSM where each state
does only a little processing, and there are many transitions -
such as in a parser reading in a large file.

The byte-code rewriting hack mentioned in a previous post
eliminates this overhead. But, I concede, is somewhat ugly.

Cheers,
Carl.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Python code written in 1998, how to improve/change it?

2006-01-19 Thread Carl Cerecke
Carl Cerecke wrote:
 Chris Mellon wrote:
 
 I'm not sure why nobody else in this thread said it, but the most
 common way of implementing state machines I've seen in Python (unless
 theres only a couple states you can manage with if/elif) is to use a
 dict to map states to callables.
 
 
 Ah. Well, my post suggested, as one option, the callables call
 each other directly.

Doh! No I didn't. And they shouldn't. Otherwise the call stack
gets out of hand. But I did suggest that each callable representing a 
state set a global variable, just before it returns, to the callable 
representing the next state to be called. Still no map required. Just a 
while loop. In any case, the function call/return is wasted cycles.

Cheers,
Carl.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Python code written in 1998, how to improve/change it?

2006-01-19 Thread Peter Hansen
Carl Cerecke wrote:
 Carl Cerecke wrote:
Ah. Well, my post suggested, as one option, the callables call
each other directly.
 
 Doh! No I didn't. And they shouldn't. Otherwise the call stack
 gets out of hand. But I did suggest that each callable representing a 
 state set a global variable, just before it returns, to the callable 
 representing the next state to be called. Still no map required. Just a 
 while loop. In any case, the function call/return is wasted cycles.

I believe the more modern approach to this is to use generators in some 
way, yield each other as the next state.  This way you avoid all almost 
all the function call overhead (the part that takes significant time, 
which is setting up the stack frame) and don't have to resort to 
bytecode hacks for better performance.

Of course, if you have a state machine with many small states each doing 
a tiny bit of processing and you're still concerned over performance, 
you probably should be looking into Pysco or Pyrex and avoid making your 
code really unreadable.

-Peter

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


Re: Python code written in 1998, how to improve/change it?

2006-01-19 Thread skip

Petr Hello, I am trying to study/understand OOP principles using
Petr Python. I have found following code http://tinyurl.com/a4zkn about
Petr FSM (finite state machine) on this list, which looks quite useful
Petr for my purposes. As this code was posted long time ago (November
Petr 1998) I would like to ask if the principles used in this code are
Petr still valid in the modern Python and if/how it can be improved
Petr (revrited) using futures of current version of Python.

A more up-to-date version of my finite state machine class (referenced in
the 1998 post you refer to) is available here:

http://orca.mojam.com/~skip/python/fsm.py

It's still in use, though I haven't written anything new with it in a year
or so.

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