The concept of state machines and tutorials for using state machines under Python

Source: Internet
Author: User
What is a state machine?

An extremely precise description of the state machine is that it is a forward graph consisting of a set of nodes and a corresponding set of transfer functions. A state machine "runs" by responding to a series of events. Each event is within the control of the transfer function belonging to the current node, where the scope of the function is a subset of the node. The function returns the "next" (perhaps the same) node. At least one of these nodes must be an end state. When the end state is reached, the state machine stops.

But an abstract mathematical description (as I've just given it) doesn't really explain the situation in which a state machine can be used to solve actual programming problems. Another strategy is to define a state machine as a mandatory programming language, where nodes are also source lines. From a practical point of view, this definition, although accurate, but it is the same as the first description, is an armchair, not practical. (This is not necessarily the case for descriptive, functional, or constraint-based languages such as Haskell, Scheme, or Prolog.) )

Let's try using examples that are better suited to the actual tasks around us. Logically, each rule expression is equivalent to a state machine, and the parser for each regular expression implements the state machine. In fact, most programmers write state machines without really taking this into account.

In the following example, we will look at the true exploratory definition of the state machine. In general, we have a number of different ways to respond to a limited set of events. In some cases, the response depends only on the event itself. In other cases, however, the appropriate action depends on the previous event.

The state machine discussed in this article is a high-level machine designed to demonstrate a programming solution for a class of problems. If it is necessary to discuss programming issues in response to the category of event behavior, then your solution is likely to be an explicit state machine.

Text Processing state machine

A programming problem that most likely calls an explicit state machine involves processing a text file. Processing a text file typically involves reading a unit of information (usually called a character or line) and then performing the appropriate action on the unit that was just read. In some cases, this processing is "stateless" (that is, each of these units contains enough information to correctly determine what action to take). In other cases, even if the text file is not completely stateless, the data has only a limited context (for example, the operation depends on no more information than the line number). However, in other common text processing problems, the input file is very "state". The meaning of each piece of data depends on the string preceding it (perhaps the string following it). Reports, mainframe data entry, readable text, programming source files, and other kinds of text files are stateful. A simple example is a line of code that might appear in a Python source file:

MyObject = SomeClass (this, so, other)

This line indicates that if there are just a few lines around this line, some of the content is different:

"How to use Someclass:myobject = SomeClass" "" "

We should know that we are in a "block reference" state to make sure that this line of code is part of a comment rather than a Python operation.

When not to use a state machine

When you start writing a processor task for any stateful text file, ask yourself what type of input you want to find in the file. Each type of entry is a candidate for a state. There are several types of these. If the numbers are large or uncertain, the state machine may not be the correct solution. (In this case, some database solutions might be more appropriate.) )

Also, consider whether you need to use a state machine. In many cases, it's best to start with a simpler approach. It may be found that even if the text file is stateful, there is an easy way to read it in chunks (where each piece is a type of input value). In fact, in a single state block, it is necessary to implement a state machine only if the transfer between text types requires content-based computation.

The following simple example illustrates the need to use a state machine. Consider the two rules used to divide a column of numbers into blocks. In the first rule, 0 in the list represents a break between blocks. In the second rule, breaks between blocks occur when the sum of elements in a block exceeds 100. Because it uses an accumulator variable to determine whether a threshold is reached, you cannot "immediately" see the boundary of the child list. Therefore, the second rule might be more appropriate for a mechanism similar to a state machine.

An example of a text file that is slightly stateful but is not well-suited for processing by a state machine is the Windows-style. ini file. This file includes a section header, comments, and many assignments. For example:

; Set the ColorScheme and userlevel[colorscheme]background=redforeground=bluetitle=green[userlevel]login=2title=1

Our example has no practical meaning, but it shows some interesting features of the. ini format.

In a sense, the type of each row is determined by its first character (possibly a semicolon, left parenthesis, or letter).
From another point of view, this format is "stateful", because the keyword "title" probably means that if it appears in each section, then there is a separate content.

You can write a text processor program with the ColorScheme state and USERLEVEL state, which still handles the assignment of each state. But it doesn't seem to be the right way to deal with this problem. For example, you can use Python code to create only natural blocks in this text file, such as:
Processing. INI file with the chunked Python code

Import    stringtxt = open (  ' Hypothetical.ini '). Read () sects = string.split (txt,   ' [')  for    sect    in sects:   # does something with sect, like Get it name # (the stuff up to '] ") and read its assignments

Or, if you prefer, you can use a single current_section variable to determine the location:
Processing. INI file to calculate Python code

For line in    open (  ' Hypothetical.ini '). ReadLines ():   if    line[0] = =   ' [': Current_ Section = line (1:-2)   elif    line[0] = =   '; ':   pass   # Ignore comments    else   : apply_ Value (current_section, line)

When to use a state machine

Now, we've decided that if the text file is "too simple" to use a state machine, let's look at the situation where the state machine needs to be used. A recent article in this column discusses the utility txt2html, which translates "smart ASCII" (including this article) into HTML. Let's recap.

"Smart ASCII" is a text format that uses some interval conventions to differentiate between types of text blocks, such as headers, regular text, quotations, and code samples. While it is easy for readers or authors to analyze the transfer between these text block types, there is no easy way for the computer to split the "smart ASCII" file into chunks of text that make up it. Unlike the. ini file example, text block types can appear in any order. There is no single delimiter in any case to separate blocks (empty lines usually separate blocks of text, but blank lines in code samples do not necessarily end code samples, and text blocks do not need to be separated by blank lines). State machines seem to be a natural solution because you need to reformat each block of text in different ways to produce the correct HTML output.

The general functions of the txt2html reader are as follows:

    • Start in the initial state.
    • Reads a line of input.
    • The row is moved to a new state or is processed in a way that is appropriate for the current state, depending on the input and current state.

This example is about the simplest scenario you will encounter, but it illustrates the following patterns that we have described:
A simple state machine input loop in Python

Global state, blocks, Bl_num, newblock#--Initialize the globalsstate = "HEADER" blocks = [""]bl_num = 0newblock = 1 fo R line in Fhin.readlines (): if state = = "header": # Blank line means new block of HEADER if BL Ankln.match (line): Newblock = 1 elif textln.match (line): Starttext (line) elif Codeln.match (line): Startcode (line ) Else:if Newblock:starthead (line) else:blocks[bl_num] = Blocks[bl_num] + Line elif state = = "TEX T ": # Blank line means new block of text if Blankln.match (line): Newblock = 1 elif headln.match (line): s Tarthead (line) elif Codeln.match (line): Startcode (line) else:if Newblock:starttext (line) Else:block S[bl_num] = Blocks[bl_num] + Line elif state = = "CODE": # blank line does no change state if Blankln. Match (line): blocks[bl_num] = Blocks[bl_num] + line elif Headln.match (line): Starthead (line) elif Textln.match (l   INE): Starttext (line)Else:blocks[bl_num] = Blocks[bl_num] + line else:raise valueerror, "unexpected input block state:" +state

You can use txt2html to download the source file from which the code is extracted (see resources). Note: The variable state is declared global, and its value is changed in a function such as Starttext (). Transfer conditions, such as Textln.match (), are regular expression patterns, but they may also be custom functions. In fact, formatting will be performed later in the program. The state machine parses only the text file into a labeled block in the blocks list.

Abstract state Machine Class

It is easy to use Python to implement abstract state machines in forms and functions. This makes the state machine model of the program more prominent than the simple condition block in the previous example (at first glance, the conditions are no different from other conditions). Also, the following classes and their associated handlers are doing well in the isolated state of operations. In many cases, this improves encapsulation and readability.
Files: statemachine.py

From    string   import    Upper  class    StateMachine   :   def    __init__   (self): Self.handlers = {} self.startstate = None self.endstates = []   def    add_state   (self, name, handler, End_state =0): name = UPPER (name) Self.handlers[name] = handler   if    end_state:self.endStates.append (name)   def    Set_start   (self, name): self.startstate = UPPER (name)   def    run   (self, cargo):   try   : handler = Self.handlers[self.startstate]   except   :   raise   "Initializationerror",   "must call. set_ Start () before. Run () "    if    not    self.endstates:   raise   " Initializationerror ",   " at Least one State must is a end_state "    while 1: (newstate, cargo) = Handler (cargo)   if    Upper (NewState)   in    self.endstates:   break    Else   : handler = Self.handlers[upper (newstate)]

The StateMachine class is actually what the abstract state machine needs. Because using Python to transfer function objects is so simple, compared to similar classes in other languages, this class requires very little use of the number of rows.

To really use the StateMachine class, you need to create some handlers for each state you want to use. The handler must conform to the pattern. It loops through the event until it is transferred to another State, at which point the handler should pass a byte group (which includes the new state name and any cargo required by the new state handler) back.

Using cargo as a variable in the StateMachine class encapsulates the data that the state handler needs (the state handler does not have to call its cargo variable). The state handler uses cargo to pass the content required by the next handler, so the new handler can take over the legacy work of the previous handler. Cargo typically includes a file handle, which allows the next handler to read more data after the previous handler has stopped. Cargo can also be a database connection, a complex class instance, or a list with several items.

Now, let's study the test sample. In this example (outlined in the following code example), cargo is just a number that continually transmits feedback to the iteration function. As long as Val is in a range, the next value of Val is always just Math_func (Val). Once the function returns an out-of-range value, the value is passed to another handler, or the state machine exits after invoking an end-state handler that does nothing. The example illustrates one thing: an event does not have to be an input event. It can also be a calculation of events (this is rare). The difference between state handlers is that they only use different tokens when outputting the events they handle. This function is relatively simple and does not necessarily use a state machine. But it is a good illustration of the concept. The code may be easier to understand than the explanation!
Files: statemachine_test.py

From StateMachine import StateMachine def ones_counter (val): print ' ones state: ' While 1:if   Val <= 0 or val >= 30:newstate = "Out_of_range";   Break Elif <= val < 30:newstate = "twenties";   Break Elif <= val < 20:newstate = "TENS";    Break Else:print "@%2.1f+"% val, val = Math_func (val) print ">>" return (NewState, Val) def Tens_counter (val): print "Tens state:", while 1:if Val <= 0 or val >= 30:newstate = "Ou   T_of_range ";   Break Elif 1 <= val < 10:newstate = "ONES";   Break Elif <= val < 30:newstate = "twenties";    Break Else:print "#%2.1f+"% val, val = Math_func (val) print ">>" return (NewState, Val) def    Twenties_counter (val): print "Twenties State:", while 1:if Val <= 0 or val >= 30:newstate =   "Out_of_range"; Break Elif 1 <= val < 10:newstate= "ONES";   Break Elif <= val < 20:newstate = "TENS";    Break Else:print "*%2.1f+"% val, val = Math_func (val) print ">>" return (NewState, Val) def Math_func (n): From math import sin return abs (sin (n)) *31 if __name__== "__main__": M = Statemachi NE () m.add_state ("ONES", Ones_counter) m.add_state ("TENS", Tens_counter) m.add_state ("twenties", Twenties_counter) m . Add_state ("Out_of_range", None, end_state=1) m.set_start ("ONES") M.run (1)
  • Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.