Skip to content

Commit 250bb81

Browse files
committed
initial commit
0 parents  commit 250bb81

File tree

5 files changed

+185
-0
lines changed

5 files changed

+185
-0
lines changed

.gitignore

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
__pycache__
2+
*.pyc
3+
*.pyo
4+
*.pyd
5+
*.so
6+
*.dll
7+
*.egg-info
8+
build/
9+
dist/
10+
*.egg
11+
*.log
12+
*.DS_Store
13+
venv/

README.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Here's the improved version of your README.md with corrected grammar and formatting:
2+
3+
# Simple Finite State Machine
4+
5+
This package provides a simple implementation of a Finite State Machine (FSM) in Python.
6+
7+
## Installation
8+
9+
To install the package, run the following command:
10+
```
11+
pip install git+https://github.com/MQ37/le-automata
12+
```
13+
14+
## Usage
15+
16+
To use the FSM package, you can follow the example provided in the `examples.py` file. The file demonstrates how to create an instance of the FSM and test it with different input words.
17+

example.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from le_automata import Machine
2+
3+
# machine that detects odd number of 'b'
4+
m = Machine(
5+
alphabet="ab",
6+
initial_state="q0",
7+
end_states=["q1"],
8+
transitions=[
9+
("q0", "a", "q0"),
10+
("q0", "b", "q1"),
11+
("q1", "a", "q1"),
12+
("q1", "b", "q0"),
13+
])
14+
15+
assert m.run("aba") is True
16+
assert m.run("abba") is False
17+
18+
19+
# machine that detects if word ends with 'abba'
20+
m = Machine(
21+
alphabet="ab",
22+
initial_state="q0",
23+
end_states=["q4"],
24+
transitions=[
25+
("q0", "b", "q0"),
26+
("q0", "a", "q1"),
27+
28+
("q1", "a", "q1"),
29+
("q1", "b", "q2"),
30+
31+
("q2", "b", "q3"),
32+
("q2", "a", "q1"),
33+
34+
("q3", "a", "q4"),
35+
("q3", "b", "q0"),
36+
37+
("q4", "a", "q1"),
38+
("q4", "b", "q0"),
39+
])
40+
41+
assert m.run("aba") is False
42+
assert m.run("abba") is True
43+
assert m.run("aabba") is True
44+
assert m.run("babba") is True
45+
assert m.run("abababababababba") is True
46+
assert m.run("abbab") is False
47+
assert m.run("abbaa") is False
48+

le_automata/__init__.py

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
class Machine:
2+
3+
def __init__(self,
4+
alphabet: str,
5+
initial_state: str,
6+
end_states: list[str],
7+
transitions: list[tuple[str, str, str]]):
8+
"""
9+
Initialize a Finite State Machine.
10+
11+
Args:
12+
alphabet (str): The alphabet of the machine.
13+
initial_state (str): The initial state of the machine.
14+
end_states (list[str]): A list of end states of the machine.
15+
transitions (list[tuple[str, str, str]]): A list of tuples representing the transitions.
16+
Each tuple should be of the form (current_state, input_symbol, next_state).
17+
"""
18+
self._alphabet: set[str] = set(alphabet)
19+
self._initial_state: str = initial_state
20+
self._end_states: set[str] = set(end_states)
21+
self._check_transitions(transitions)
22+
self._transitions = self._build_transision_dict(transitions)
23+
24+
def _build_transision_dict(self,
25+
transitions: list[tuple[str, str, str]]
26+
) -> dict[tuple[str, str], str]:
27+
"""
28+
Build a dictionary representation of the transitions.
29+
30+
Args:
31+
transitions (list[tuple[str, str, str]]): A list of tuples representing the transitions.
32+
33+
Returns:
34+
dict[tuple[str, str], str]: A dictionary where the key is a tuple of (current_state, input_symbol)
35+
and the value is the next_state.
36+
"""
37+
_transitions = {}
38+
for (q, a, qn) in transitions:
39+
_transitions[(q, a)] = qn
40+
return _transitions
41+
42+
def _check_transitions(self,
43+
transitions: list[tuple[str, str, str]]):
44+
"""
45+
Check if the transitions are valid.
46+
47+
Args:
48+
transitions (list[tuple[str, str, str]]): A list of tuples representing the transitions.
49+
50+
Raises:
51+
ValueError: If any state does not handle a symbol from the alphabet.
52+
"""
53+
state_alphabet = {}
54+
for (q, a, qn) in transitions:
55+
if q not in state_alphabet:
56+
state_alphabet[q] = self._alphabet - {a}
57+
continue
58+
state_alphabet[q] = state_alphabet[q] - {a}
59+
60+
for q, a in state_alphabet.items():
61+
if a:
62+
raise ValueError(f"Node '{q}' does not handle symbol '{a}'")
63+
64+
def qn(self, q: str, a: str) -> str:
65+
"""
66+
Get the next state from the current state q and symbol a.
67+
68+
Args:
69+
q (str): The current state.
70+
a (str): The input symbol.
71+
72+
Returns:
73+
str: The next state.
74+
"""
75+
return self._transitions[(q, a)]
76+
77+
def run(self, word: str) -> bool:
78+
"""
79+
Check if the input word is valid for this machine.
80+
81+
Args:
82+
word (str): The input word to check.
83+
84+
Returns:
85+
bool: True if the word is valid, False otherwise.
86+
87+
Raises:
88+
ValueError: If the word contains a symbol not in the alphabet.
89+
"""
90+
if not set(word).issubset(self._alphabet):
91+
raise ValueError(f"Word '{word}' is not a subset of alphabet '{self._alphabet}'")
92+
93+
states = [self._initial_state]
94+
for a in word:
95+
q = states[-1]
96+
qn = self.qn(q, a)
97+
states.append(qn)
98+
99+
q = states[-1]
100+
return q in self._end_states

setup.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from setuptools import setup, find_packages
2+
3+
setup(
4+
name='le-automata',
5+
version='0.1.0',
6+
packages=find_packages(),
7+
)

0 commit comments

Comments
 (0)