Python Programming
 
Forums: » Register « |  User CP |  Games |  Calendar |  Members |  FAQs |  Sitemap |  Support | 
User Name:
Password:
Remember me
Go Back   Dev Shed ForumsProgramming LanguagesPython Programming
Receive the tools necessary to be the rock star of your field. Our 12-month program teaches you the evolving world of multi-channel marketing as well as the complex issues and opportunities found in the industry.

ASP Free and Iron Speed Designer are giving away $5,500+ in FREE licenses. Iron Speed's RAD CASE toolset can save up to 80% of your coding time. One free license per week, one perpetual license per month!
Download and Activate to enter!

Web development can be a daunting task, even for specialists. There is a lot of information to absorb and a lot of technologies to learn in order to manage a superior website. When trying to learn the ropes, developers need a reliable source to introduce new ideas that can be easily implemented. When working on large projects, even web veterans may run into a technology or an aspect of a technology that they are unfamiliar with.

Learn More!


Download to Enter
| Contest Rules

Tutorials | Forums

Reply
Add This Thread To:
  Del.icio.us   Digg   Google   Spurl   Blink   Furl   Simpy   Y! MyWeb 
Thread Tools Search this Thread Rate Thread Display Modes
 
Unread Dev Shed Forums Sponsor:
  #1  
Old August 21st, 2008, 03:23 PM
Schol-R-LEA's Avatar
Schol-R-LEA Schol-R-LEA is offline
Commie Mutant Traitor
Dev Shed Intermediate (1500 - 1999 posts)
 
Join Date: Jun 2004
Location: Norcross, GA (again)
Posts: 1,713 Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level)Schol-R-LEA User rank is General 8th Grade (Above 100000 Reputation Level) 
Time spent in forums: 1 Month 2 Weeks 1 Day 33 m 13 sec
Reputation Power: 1495
Opinions wanted: is this worth making a SF project out of?

Mods: I wasn't certain if it should be in the language-specific forum or one of the discussion forums. If this would be better off in another message board, feel free to move it.

Earlier this year, I took a course an undergraduate course in compiler design, and opted to use Python as my implementation language - the class was to focus on the design aspects, not performance, so I concluded that I would rather use a language which wouldn't fight back so much as Java or C++ would when it came to string handling. While this may have been a mistake in the end - the time I saved not writing a lot of string handling code was lost again by some rather overly ambitious design approaches - in the process of it, I implemented a more or less general string-driven finite automata library.

I am now wondering if this would be something which would be of use to others, so I am considering creating a project on SourceForge for it. I would like to know if others think this would be worth the trouble to share or not, and if so, what changes or refinements should be made to make it a viable open-source library. I am concerned that it is too small and basic to be worth making a library out of. Conversely, it may be too heavyweight to be practical, since FSMs are usually implemented in much simpler manners than the one I used. Also, the design is not especially Pythonic, being rather clumsy and Java-esque in it's initialization specifically. Finally, it may not be suited to a lot of things people might want an FSM for - while the design is almost general, it is still designed primarily for tokenizing a stream of characters, which may limit its applicability.

The library consists of two classes, a State class and a StateMachine class. A State object consists of a key, a table of input/state transition pairs, a flag whether the state can be a final state, a type for the type of token it returns (if any), and a pushback number (most likely, I will replace these with some more general sort of completion value, though just how I haven't decided). The class has four class constants: three representing the common transition states (accept, error, and other), one serving a common start state key (used by the StateMachine class), and one used to indicate a default transition state for input not otherwise specified (if none is given, it assumes an error state). In addition to a constructor and a string-representation method, t has one instance method, next(), which takes an input string and returns a tuple with state to which it would transition, the final-state flag, a token, (if it is an accept state), and a pushback number (if it is at a final state that ends one or more characters back).

The StateMachine object consists of a table of state keys (right now, it only uses numbers, but I think that allowing named states may prove valuable later) and states, State object representing the current state of the FSM, and a flag indicating if the table is 'locked' or not (that is, the table has been completely populated). The StateMachine is constructed with a required start state, and new states are added to the table using the add() method, and new states can be added until lock() is called. There is a locked() method to test whtehr it is locked or not. Once the table is locked, the transition() method can be used to step through the input stream, taking one character at a time as input. It returns the accept flag, the token, and the pushback number returned by the call to current_state.next(); on a error state, it returns State.ERROR for the token.

Here is the code as it currently exists:
Python Code:
Original - Python Code
  1. from typecheck import *
  2.  
  3. __title__ = 'AutonomousCollective'
  4. __author__ = 'Joseph Osako'
  5. __version__ = '0.1'
  6. __date__ = '2008:08:21'
  7.  
  8. __credits__ += """Uses Dimitri Dvoinikov's typecheck decorator library"""
  9. __credits__ += """<http://www.targeted.org/python/recipes/typecheck.py>"""
  10. __credits__ += """for method typechecking.\n"""
  11.  
  12. class State (object):
  13.     """ Class representing the states of a Finite State Automata.
  14.    
  15.     Instance Variables:
  16.         transitions: dictionary (string:integer)
  17.         final: boolean
  18.         toktype: Object
  19.         pushback: integer
  20.    
  21.     State object consist of four properties: an a-list of accepted input
  22.     strings and numeric identifiers for the succeeding states, a flag
  23.     indicating if the state can lead to a final (accept or reject) state,
  24.     the condition returned when accepted, and a number of characters which
  25.     the recognizer should push back by. A State can take an input character
  26.     and return an identifying number for the corresponding successor State.
  27.    
  28.     The State class has five class constants. Four of these represent common
  29.     states for all state machines (START, ACCEPT, ERROR, and DEFAULT). The
  30.     last represents a default input.
  31.     """
  32.     START = 0
  33.     ACCEPT = -1
  34.     ERROR = -2
  35.     DEFAULT = -3
  36.     OTHER = "__OTHER__"
  37.  
  38.     def __init__(self, transitions, final = False, \
  39.                 toktype = ERROR, pushback = 0):
  40.         """ Constructor - create a State object.
  41.        
  42.        @params:
  43.         transitions - lookup table of inputs and states
  44.         final - final/non-final state flag (default false)
  45.         toktype - type of token recognized on accept (default None)
  46.         pushback - number of chars to push back after accept (default 0)
  47.        
  48.         Initializes the instance variables for the new object.
  49.        """
  50.         self.__final, self.__toktype = final, toktype
  51.         self.__transitions = transitions
  52.         self.__pushback = pushback
  53.    
  54.     @takes("State", str)
  55.     @returns(tuple)
  56.     def next(self, input):
  57.         """ Return the successive state based on the input character.
  58.        
  59.          The next() method is the primary behavior of the State object.
  60.         It returns a tuple containing the successor state, whether it
  61.         is a final state or not, the recognized result (if any) and the
  62.         amount of pushback (if any).
  63.        
  64.         next() takes a single character input: it will reject any string not
  65.         of size one, returning the number of extraneous characters in
  66.         'pushback'. The method first tries to match the character to a
  67.         state through a simple lookup, on the assumption that most transitions
  68.         will be based on a single possible input. If this does not succeed,
  69.         it will try to match the characters against keys with multiple
  70.         characters; this allows a given state transition to give a set of
  71.         possible matches (e.g., all alphanumeric characters) rather than
  72.         requiring a separate key for each character. If this does not succeed,
  73.         it then checkes to see if there is a default state set. If none of these
  74.         find a match, the method returns an error state.
  75.        
  76.         If a successor is found, the method then checks to see if the successor
  77.         is either ACCEPT or ERROR. If it is ACCEPT, it returns the ACCEPT
  78.         constant, a true final value, the type of the recongized token, and
  79.         a pushback amount. If it is ERROR, it returns the ERROR constant, a
  80.         true final value, ERROR as the token type, and the pushback amount.
  81.         Otherwise, it returns the successor and false/None/0 for the remainder.
  82.        """
  83.         if len(input) != 1:         # check validity of the input
  84.             return self.ERROR, False, self.ERROR, len(input) - 1
  85.        
  86.         # try to match the input to a transition
  87.         successor = None
  88.         if input in self.__transitions:     # check single-character key match
  89.             successor = self.__transitions.get(input)
  90.         else:
  91.             for s in self.__transitions:    # check multi-char keys
  92.                 if input in s and s != self.OTHER:
  93.                     successor = self.__transitions.get(s)
  94.                     break
  95.             if successor is None:           # use default transition, if any
  96.                 successor = self.__transitions.get(self.OTHER)
  97.                 if successor is None:
  98.                     return self.ERROR, False, self.ERROR, 0
  99.                
  100.         # successor found, test if it is either an ACCEPT or ERROR state.
  101.         if successor == self.ACCEPT:    # ACCEPT
  102.             return self.ACCEPT, True, self.__toktype, self.__pushback
  103.         elif successor == self.ERROR:   # ERROR
  104.             return self.ERROR, True, self.ERROR, self.__pushback
  105.         else:
  106.             # not a final state, return the successor state
  107.             return successor, False, None, 0
  108.        
  109.         def __str__(self):
  110.             """ Basic string representation of a State."""
  111.             return str(self.__transitions)
  112.            
  113.  
  114.  
  115. class StateMachine(object):
  116.     """ Generalized Finite State Automata class.
  117.    
  118.     Instance Variables:
  119.         states: a lookup table of state numbers and states
  120.         current_state: the current state of the FSM
  121.         locked: flag whether all of the states are set.
  122.    
  123.     A FSM consists of a set of States, the current State, and a flag
  124.     indicating if all States have been added to the set of states. Until
  125.     the FSM is locked, new states may be added. The State machine begins
  126.     in the Start state, and accepts input until it either accepts or rejects
  127.     the input string. When it reaches one of these final states, it returns
  128.     the type of the recongized string and resets itself to the Start state.
  129.    """
  130.     def __init__(self, start_state):
  131.         """ Constructor - create a FSM object.
  132.        
  133.         Adds the initial (Start) state, sets the current state to Start,
  134.         and clears the locked flag.
  135.        """
  136.         self.__states = {State.START: start_state}
  137.         self.__locked = False
  138.         self.__current_state = self.__states[State.START]
  139.    
  140.    
  141.     @takes("StateMachine", int, State)
  142.     def add(self, key, state):
  143.         """ Add a new state to the state table.
  144.        
  145.         The add() method takes a state identifier key and a new State
  146.         object. If the table is not locked, and there is no State
  147.         by that number already, it adds state to the state table
  148.         and returns true. Otherwise, it returns false.
  149.        """
  150.         if self.__locked:
  151.             return False   # do not allow any states to be added
  152.         elif key in self.__states:  # state with that ID already in table
  153.             return False
  154.         else:
  155.             self.__states[key] = state
  156.             return True
  157.    
  158.     def lock(self):
  159.         """ Locks state table so no new states can be added."""
  160.         lock = True
  161.        
  162.     @returns(tuple)
  163.     def transition(self, input):
  164.         """ Perform a state transition, and if it is a final
  165.         state, return success or failure and the recognized token type.
  166.        """
  167.         # get the successor state of the current state, if any
  168.         successor, final, token, pb = self.__current_state.next(input)
  169.        
  170.         # see if the state has finalized, reset the current state and
  171.         # determine if it accepted or rejected the input string
  172.         if final:
  173.             self.__current_state = self.__states[State.START]
  174.            
  175.             if successor == State.ACCEPT:   # string accepted
  176.                 return True, token, pb
  177.             else:                           # string rejected
  178.                 return True, State.ERROR, pb
  179.         # if the state has not finalized, see if it is a 'non-final' error
  180.         # state and return an error value if it is. This will only occur if
  181.         # the input is not valid.
  182.         elif successor == State.ERROR:
  183.             self.__current_state = self.__states[State.START]
  184.             return True, State.ERROR, pb
  185.         # if the state is not final and not an error, advance to the next state
  186.         else:
  187.             self.__current_state = self.__states[successor]
  188.             return False, 0, 0
  189.    
  190.     def __str__(self):
  191.         """Return a simple string representation of the FSM for debugging. """
  192.         return str(self.__states)
  193.    
  194. ################################
  195. if __name__ == "__main__":
  196.     pass

I have attached a copy of the actual project it had been part of for anyone who wants a reference point in how I intended it to be used.

Any and all (constructive) comments and critiques would be helpful. At the moment, I have tentatively given the projec the name 'AutonomousCollective', but any other suggestions would be appreciated (though I'd prefer something with either a MP or H2G2 theme - the latter to match th name of the original project). TIA.
Attached Files
File Type: zip suntiger-0.0.1.zip (73.7 KB, 218 views)
__________________
Rev First Speaker Schol-R-LEA;2 JAM LCF ELF KoR KCO BiWM TGIF
#define KINSEY (rand() % 7) λ Scheme is the Red Pill
Scheme in ShortUnderstanding the C/C++ Preprocessor
Taming PythonA Highly Opinionated Review of Programming Languages for the Novice, v1.1

FOR SALE: One ShapeSystem 2300 CMD, extensively modified for human use. Includes s/w for anthro, transgender, sex-appeal enhance, & Gillian Anderson and Jason D. Poit clone forms. Some wear. $4500 obo. tverres@et.ins.gov

Last edited by Schol-R-LEA : August 23rd, 2008 at 04:13 PM.

Reply With Quote
  #2  
Old August 25th, 2008, 01:46 AM
NovaX NovaX is offline
Contributing User
Dev Shed Novice (500 - 999 posts)
 
Join Date: Jul 2005
Location: Bay Area, California
Posts: 841 NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level)NovaX User rank is General 11st Grade (Above 100000 Reputation Level) 
Time spent in forums: 3 Weeks 5 Days 12 h 59 m 16 sec
Reputation Power: 1679
Send a message via ICQ to NovaX Send a message via Yahoo to NovaX Send a message via Google Talk to NovaX
I think this implementation is pretty good for what its trying to do, but to be a full-fledged library you need to generalize for 3+ different use-cases. I think you'll find faults and either fix them or decide to limit the scope of the library to target a narrow nitch. Both are excellent choices, but force drastically different design choices.

As per your private message regarding my assertion that states should not perform work or provide the transition logic, let me explain why. A very common use-case is for workfow engines: travel booking, ticket tracking (support, bugs, etc), data processing, and so on. If your task knows where to go next, then this would leak across different workflows. If you specify it on a per flow basis, you're still making the execution brittle. You'll find developers making implicit data dependancies across tasks or creating bizaar flows that loop back through states to arrive at the desired final one. You'll even find workflows being used where they shouldn't be (e.g. routing of an ESB). When you make states manage the tranitioning, you put them in control of the framework. In effect, you are inverting the framework's implicit inversion of control.

I think your project works elegantly for a small, discrete set of states. But what if you have dozens? Will there be duplicate state logic but with different transitions, or the same state with many transitions (most of which are not acceptable given the flow)? Overall, it gets messier and messier the bigger the state machine grows.

Your usage makes me think of Turing machines and push-down automata. Clients of those tend not to have too many states either, and it would fit well with your usage as well. So it might be useful as a pre-rolled version to allow others to plug into. Or at least be a fun excersize to simulate different types of FSA designs. Even a package of those could be useful, given the very special-purpose nature of them.

I think the first thing to do is determine what class of problems you want to solve and then rework the design as needed.

Reply With Quote
Reply

Viewing: Dev Shed ForumsProgramming LanguagesPython Programming > Opinions wanted: is this worth making a SF project out of?


Thread Tools  Search this Thread 
Search this Thread:

Advanced Search
Display Modes  Rate This Thread 
Rate This Thread:


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
View Your Warnings | New Posts | Latest News | Latest Threads | Shoutbox
Forum Jump

Forums: » Register « |  User CP |  Games |  Calendar |  Members |  FAQs |  Sitemap |  Support | 
  
 


Powered by: vBulletin Version 3.0.5
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.

© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 10 - Follow our Sitemap