====== 4. Regular expressions ======
===== 4.1. Formation rules (concatenation, reunion, Kleene star) =====
**4.1.1.**
$math[A=\{ 0^{2k} \mid k \geq 1 \}]
$ B = \{0, \epsilon \}$
\\
$ AB = ? $
\\
**4.1.2.**
$math[A = \{ 0^n 1^n \mid n \geq 1 \}]
\\
$ B = \{ 1^n \mid n \geq 1 \} $
\\
$ AB = ? $ \\ $ BA = ? $
**4.1.3.**
$ A = \emptyset $
\\
$ B = \{ 1^n \mid n \geq 1 \} $
\\
$ AB = ? $
\\
$ A^* = ? $
\\
$ B^* = ? $
\\
===== 4.2. Writing Regular Expressions =====
**4.2.1.** Write a regular expression for the language of arithmetic expressions containing +, * and numbers.
**Hint:** you can abbreviate $ 0 \cup 1 \cup ... \cup 9 $ by $ [0-9] $
**4.2.2.** Write a regular expression for $ L = \{ \omega \text{ in } \text{{0,1}} ^* \text{ | EVERY sequence of two or more consecutive zeros appears before ANY sequence of two or more consecutive ones} \} $
**4.2.3.** Write a DFA for $ L(( 10 \cup 0) ^* ( 1 \cup \epsilon )) $
**4.2.4.** Write a regular expression which generates the accepted language of A:
{{:lfa:graf1.png?200|}}
**4.2.5.** Simplify the regular expression you found.
**4.2.6.** Describe as precisely as possible the language generated by $math[(1 \cup 1(01^*0)1)^*]
===== 4.3. Regex implementation (Python) =====
==== Inheritance and isinstance ====
Python supports multiple class inheritance, however, in the spirit of it's dynamical typing strategy, it does not enforce method implementation. Hence - interfaces or abstract classes do not exist per se.
The following example illustrates class inheritance, as well as ''isinstance'':
class Point2D:
# the class constructor.
def __init__(self,x,y):
self.x = x
self.y = y
def fun(self,a):
return a+1
# class Point3D inherits Point2D
class Point3D(Point2D):
def __init__(self,x,y,z):
# using super, we call the parent class constructor
super().__init__(x,y)
# an alternative syntax with the same effects:
# super(Point3D,self).__init__(x,y)
self.z = z
def fun(self,a):
return a + 2
p = Point3D(1,2,3)
print(p.fun(1))
# another illustration of super - we use it to call fun from the parent class.
# super(X,O).f() will call function f from the __parent__ of type X, of object O.
print(super(Point3D,p).fun(1))
# usage of isinstance to see if an object is an instance of a class (or it's parent)
if isinstance(p,Point3D):
print("Point3D")
if isinstance(p,Point2D):
print("Point2D")
**4.3.1.** Write a class hierarchy for Regular Expressions, and add the ''__str__'' function for each of them. Implement a constructor which reads a Regular Expression in **Prenex form**, from a string. The prenex form is illustrated below via examples:
# a*
s = "STAR a"
# ab
s = "CONCAT a b"
# a U b
s = "UNION a b"
# (a U b)*c*
s = "CONCAT STAR UNION a b STAR c"
Hints:
* To properly read a regular expression, you need a **state** of the parsing process, as well as information about what was parsed. What kind of data-structure is necessary for this step? This data-structure will be widely-used later, when we discuss Push-down Automata.
The following table illustrates the string parsing and stack contents (ADT style) for the last example.
^ String contents ^ Stack ^
| ''CONCAT STAR UNION a b STAR c'' | Void |
| ''STAR UNION a b STAR c'' | Push(Concat(?,?),Void) |
| ''UNION a b STAR c'' | Push(Star(?), Push(Concat(?,?),Void)) |
| ''a b STAR c'' | Push(Union(?,?), Push(Star(?), Push(Concat(?,?),Void))) |
| ''b STAR c'' | Push(a, Push(Union(?,?), Push(Star(?), Push(Concat(?,?),Void)))) |
| ''b STAR c'' | Push(Union(a,?), Push(Star(?), Push(Concat(?,?),Void))) |
| ''STAR c'' | Push(b, Push(Union(a,?), Push(Star(?), Push(Concat(?,?),Void)))) |
| ''STAR c'' | Push(Union(a,b), Push(Star(?), Push(Concat(?,?),Void))) |
| ''STAR c'' | Push(Star(Union(a,b)), Push(Concat(?,?),Void))) |
| ''STAR c'' | Push(Concat(Star(Union(a,b)),?),Void))) |
| ''c'' | Push(Star(?),Push(Concat(Star(Union(a,b)),?),Void)))) |
| | Push(c, Push(Star(?),Push(Concat(Star(Union(a,b)),?),Void))))) |
| | Push(Star(c),Push(Concat(Star(Union(a,b)),?),Void)))) |
| | Push(Concat(Star(Union(a,b)),Star(c)),Void)))) |
The string has been consumed and the stack holds one expression, thus the result is: ''Concat(Star(Union(a,b)),Star(c))''.
===== 4.4. Regex implementation (Haskell) =====
**4.4.1.** Implement the ADT ''Regex''.
**4.4.2.** Enrol the type in class ''Show''.
**4.4.3.** Write a function which takes a string containing a regex in prenex form (see the Python exercises) and returns a ''Regex''. ***Hint:** use two mutually recursive functions and see the hints regarding the stack from the Python implementation section.