Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
lfa:lab02-dfa [2020/10/19 12:27] dmihai [1. Writing DFAs] |
lfa:lab02-dfa [2021/10/13 16:13] (current) ioana.georgescu [Classes in Python] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Deterministic finite automata ====== | + | ====== 2. Deterministic finite automata ====== |
- | ==== 1. Writing DFAs ==== | + | ===== Classes in Python ===== |
- | 1.1. Write a DFA which accepts the language $ L=\{w \in \{0,1\}^* \text{ | w contains an odd number of 1s} \} $ | + | |
- | Hint: | + | |
- | * in a DFA, delta is total! | + | |
- | 1.2 Define a DFA which accepts arithmetic expressions. Consider the following definition for arithmetic expressions: | + | Python supports a limited version of Object-Oriented programming, which includes **class definitions** and **inheritance**. The concept of **interface** and **interface implementation** is absent. Hence, when inheriting a function, it is the job of the programmer to make sure a method is overloaded correctly (otherwise it is just another definition of the class). |
- | <code> | + | Below, we illustrate some examples of Python's object-oriented idiom: |
- | <expr> ::= <var> | <expr> + <expr> | <expr> * <expr> | + | |
- | <var> ::= a | b | + | <code python> |
+ | class Example: | ||
+ | # the class constructor. | ||
+ | def __init__(self,param1,param2): | ||
+ | # the keyword self is similar to this from Java | ||
+ | # it is the only legal mode of initialising and referring class member variables | ||
+ | self.member1 = param1 | ||
+ | # here member2 is a local variable, which is not visible outside of the constructor | ||
+ | member2 = param2 | ||
+ | |||
+ | def fun(self): | ||
+ | # a member function must always refer self as shown here. Otherwise it is just a function | ||
+ | # defined in the scope of the class, not a member function. | ||
+ | return 0 | ||
+ | |||
+ | def plus(self,x,y): | ||
+ | return x + y | ||
+ | |||
+ | # this is the equivalent of Java's toString method | ||
+ | def __str__(self): | ||
+ | string = ... | ||
+ | return string | ||
+ | |||
+ | #global scope | ||
+ | |||
+ | #class instantiation | ||
+ | e = Example(1,2) | ||
+ | |||
+ | #method calls: | ||
+ | e.fun() | ||
+ | |||
+ | print(e) | ||
</code> | </code> | ||
- | Hint: | + | ===== 2.1. The class Dfa (Python) ===== |
- | * how would you define the alphabet for the DFA? | + | |
- | * can <expr> be the empty string? | + | **2.1.1.** Define the class ''Dfa'' which encodes deterministic finite automata. It must store: |
+ | * the alphabet | ||
+ | * the delta function | ||
+ | * the initial state | ||
+ | * the set of final states | ||
+ | |||
+ | **Optional:** you may consider defining a class ''State'' to encode more general Dfas where states are not confined to integers. This will be useful later in your project. However, you will need to define a hash-function in order to use dictionaries over states. More details, google ''hashing in Python''. | ||
+ | |||
+ | **2.1.2.** Define a constructor for Dfas, which takes a multi-line string of the following form: | ||
- | ==== 2. Implementing DFAs ==== | ||
- | Consider the following encoding of a DFA: | ||
<code> | <code> | ||
- | <number_of_states> | + | <initial_state> |
- | <list_of_final_states> | + | <state> <char> <state> |
- | <state> <symbol> <state> | + | ... |
+ | ... | ||
+ | <final_state_1> <final_state_2> ... <final_state_n> | ||
</code> | </code> | ||
+ | |||
+ | where: | ||
+ | * the initial state is given on the first line of the input | ||
+ | * each of the subsequent lines encode transitions (states are integers) | ||
+ | * the last line encodes the set of final states. | ||
Example: | Example: | ||
<code> | <code> | ||
- | 4 | + | 0 |
- | 2 3 | + | |
0 a 1 | 0 a 1 | ||
1 b 2 | 1 b 2 | ||
2 a 0 | 2 a 0 | ||
+ | 1 2 | ||
</code> | </code> | ||
- | 2.1. Write a function which takes a DFA encoding as above and returns a DFA representation. Define a class "DFA". | + | **2.1.3.** Implement a member function which takes a **configuration** (pair of state and rest of word) and returns the next configuration. |
- | 2.2. Add a method accept which takes a word and returns true if it is accepted by the DFA | + | **2.1.4.** Implement a member function which verifies if a word is **accepted** by a Dfa. |
- | 2.3. Add a method step with takes a DFA configuration and returns the "next-step" configuration of the DFA. How is a configuration defined? | + | ===== 2.2. The Dfa Algebraic Datatype (Haskell) ===== |
- | 2.4(*) Write a method which: | + | During the lecture, Dfa states were encoded as integers. As we will soon see, we will need to provision our implementation, so that other types may encode states. |
- | * takes a list of DFAs $ a_1, a_2, ..., a_n$ | + | * Should the ADT ''DFA'' be **monomorphic** or **polymorphic**? |
- | * takes a string $ s$. We know the string consists of a sequence of words, each accepted by some dfa in the list. | + | |
- | * returns a list of pairs $ (w_1,a_1), ...(w_i,a_i) ... (w_n,a_n)$ such that $ w_1w_2... w_n = s$ and the dfa $ a_i$ accepts word $ w_i$, for each i from 1 to n. | + | |
- | Example: | + | Also, we shall require a few constraints on what a proper state type should be. States should be: (i) **comparable** via equality, (ii) support an **ordering**. Later on we may add: |
+ | * new constraints | ||
+ | * **operations** which are specific to states only. | ||
+ | |||
+ | How can we **group** all these constraints and support for future state operations? | ||
+ | |||
+ | Finally, in order to encode transitions and sets, we need the ''Map'' and ''Set'' datatypes which are available in their own modules: | ||
+ | <code haskell> | ||
+ | {- We do import the data constructor Map and the infix function (!) as unqualified, to make them easier to use (Map.Map and Map.(!) is not very legible) -} | ||
+ | import Data.Map (Map, (!)) | ||
+ | {- | ||
+ | the keyword qualified forces us to prefix each function call from the module Data.Map with "Map." this is useful for two reasons: | ||
+ | - some function names overlap | ||
+ | - it makes the code more legible, by making the programmer aware of the module location of the called function | ||
+ | -} | ||
+ | import qualified Data.Map as Map | ||
+ | |||
+ | import Data.Set (Set) | ||
+ | import qualified Data.Set as Set | ||
+ | </code> | ||
+ | |||
+ | <blockquote>** How to choose between lists and sets during the implementation?** | ||
+ | * Do you need to make sure elements are unique? (go for sets) | ||
+ | * Do you need to iterate a lot over elements, and the collection size is not really big (go for lists) | ||
+ | </blockquote> | ||
+ | **2.2.1.** Implement the datatype ''DFA''. It must store: | ||
+ | * the alphabet | ||
+ | * the delta function | ||
+ | * the initial state | ||
+ | * the set of final states | ||
+ | |||
+ | |||
+ | ---- | ||
+ | |||
+ | **2.2.2.** Define a function which takes a string of the following form showed below, and returns a DFA **with states as integers**: | ||
<code> | <code> | ||
- | l = [a1, a2, a3] | + | <initial_state> |
- | ''' | + | <state> <char> <state> |
- | a1 accepts sequences of digits [0-9] | + | ... |
- | a2 accepts sequences of lowercase symbols [a-z] | + | ... |
- | a3 accepts operands (+ and *) | + | <final_state_1> <final_state_2> ... <final_state_n> |
- | ''' | + | </code> |
- | s = "var+40*2300" | + | |
- | our function returns: | + | where: |
- | [("var",2), ("+",3), ("40",1), ("*",3), ("2300",1)] | + | * the initial state is given on the first line of the input |
+ | * each of the subsequent lines encode transitions (states are integers) | ||
+ | * the last line encodes the set of final states. | ||
+ | |||
+ | Example: | ||
+ | <code> | ||
+ | 0 | ||
+ | 0 a 1 | ||
+ | 1 b 2 | ||
+ | 2 a 0 | ||
+ | 1 2 | ||
</code> | </code> | ||
+ | |||
+ | **Hint:** Use ''splitBy'' from PP. | ||
+ | ---- | ||
+ | |||
+ | |||
+ | **2.2.3.** Enroll the DFA type in class Show. | ||
+ | |||
+ | **2.2.4.** Implement function which takes a DFA and a **configuration** (pair of state and rest of word) and returns the next configuration. | ||
+ | |||
+ | **2.2.5.** Implement a function which verifies if a word is **accepted** by a Dfa. What kind of general list-operation best matches the accepting process? | ||
+ | |||
+ | |||
+ | ===== 2.3. Dfa practice ===== | ||
+ | |||
+ | Write Dfas and test them using your implementation, for the following languages: | ||
+ | |||
+ | * **2.3.1.** $ L=\{w \in \{0,1\}^* \text{ | w contains an odd number of 1s} \} $ | ||
+ | * **2.3.2.** The language of binary words which contain **exactly** two ones | ||
+ | * **2.3.3.** The language of binary words which encode odd numbers (the last digit is least significative) | ||
+ | * **2.3.4.** (hard) The language of words which encode numbers divisible by 3. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ |