NFA: implement transitions as dict instead of function
Some checks failed
test / test (push) Has been cancelled

This commit is contained in:
Yuri Tatishchev 2025-02-27 22:43:33 -08:00
parent bbfa3cad87
commit 1deb18ef22
Signed by: CaZzzer
GPG Key ID: E0EBF441EA424369

View File

@ -1,37 +1,110 @@
import gleam/list import gleam/dict.{type Dict}
import gleam/string
import gleam/set.{type Set}
import gleam/io import gleam/io
import gleam/list
import gleam/result
import gleam/set.{type Set}
import gleam/string
//pub type State { pub type State =
// State(Int) Int
//}
pub type State = Int pub type AlphabetChar =
String
//pub type AlphabetChar { pub type NFATransitionKey =
// AlphabetChar(String) #(State, AlphabetChar)
//}
pub type AlphabetChar = String pub type NFATransitionFunction =
Dict(NFATransitionKey, Set(State))
pub type NFAError { pub type NFAError {
NoTransition NoTransition
} }
pub type NFA { pub type NFA {
NFA(states: Set(State), alphabet: Set(AlphabetChar), transition_func: fn(Set(State), AlphabetChar) -> Set(State), init_state: State, accepting_states: Set(State)) NFA(
states: Set(State),
alphabet: Set(AlphabetChar),
transitions: NFATransitionFunction,
init_state: State,
accepting_states: Set(State),
)
} }
//pub type DFAViolation { pub type DFAViolation {
// NotAllTransitionsDefined(state: State, missing_chars: Set(AlphabetChar)) NotAllTransitionsDefined(state: State, missing_chars: Set(AlphabetChar))
// MultiplePossibleTransitions(state: State, duplicate_chars: Set(AlphabetChar)) MultiplePossibleTransitions(state: State, duplicate_chars: Set(AlphabetChar))
//} }
pub fn is_dfa(nfa: NFA) -> Result(Nil, List(DFAViolation)) {
let violations =
nfa.states
|> set.map(fn(state) {
let missing_transitions =
nfa.alphabet
|> set.filter(fn(char) {
nfa.transitions
|> dict.get(#(state, char))
|> result.unwrap(set.new())
|> set.is_empty
})
let multi_transitions =
nfa.transitions
|> dict.filter(fn(_key, val) { set.size(val) > 1 })
|> dict.keys
|> list.map(fn(transition_key) { transition_key.1 })
|> set.from_list
let missing_transitions = case set.size(missing_transitions) {
count if count > 0 -> [
NotAllTransitionsDefined(state, missing_transitions),
]
_ -> []
}
let multi_transitions = case set.size(multi_transitions) {
count if count > 0 -> [
MultiplePossibleTransitions(state, multi_transitions),
]
_ -> []
}
list.append(missing_transitions, multi_transitions)
})
|> set.to_list
|> list.flatten
case list.length(violations) {
count if count > 0 -> Error(violations)
_ -> Ok(Nil)
}
}
fn exec_transition(
transitions: Dict(#(State, AlphabetChar), Set(State)),
states: Set(State),
char: AlphabetChar,
) -> Set(State) {
states
|> set.filter(fn(state) { dict.has_key(transitions, #(state, char)) })
|> set.fold(set.new(), fn(new_states, state) {
new_states
|> set.union(
transitions
|> dict.get(#(state, char))
|> result.unwrap(set.new()),
)
})
}
pub fn check_string(nfa: NFA, input: String) -> Bool { pub fn check_string(nfa: NFA, input: String) -> Bool {
let final_states = input let final_states =
|> string.to_graphemes input
|> list.fold(set.from_list([nfa.init_state]), nfa.transition_func) |> string.to_graphemes
|> list.fold(set.from_list([nfa.init_state]), fn(states, char) {
exec_transition(nfa.transitions, states, char)
})
!set.is_disjoint(nfa.accepting_states, final_states) !set.is_disjoint(nfa.accepting_states, final_states)
} }
@ -39,27 +112,50 @@ pub fn check_string(nfa: NFA, input: String) -> Bool {
pub fn even_a() -> NFA { pub fn even_a() -> NFA {
let states = set.from_list([0, 1]) let states = set.from_list([0, 1])
let alphabet = set.from_list(["a"]) let alphabet = set.from_list(["a"])
let transition_func = fn (states, char) { let transitions =
states dict.from_list([
|> set.fold(set.new(), fn(n_states, state) { #(#(0, "a"), set.from_list([1])),
case state, char { #(#(1, "a"), set.from_list([0])),
0, "a" -> set.insert(n_states, 1) ])
1, "a" -> set.insert(n_states, 0)
_, _ -> n_states
}
})
}
let init_state = 0 let init_state = 0
let accepting_states = set.from_list([0]) let accepting_states = set.from_list([0])
NFA(states:, alphabet:, transition_func:, init_state:, accepting_states:) NFA(states:, alphabet:, transitions:, init_state:, accepting_states:)
}
pub fn ends_with_b() -> NFA {
let states = set.from_list([0, 1, 2])
let alphabet = set.from_list(["a", "b"])
let transitions =
dict.from_list([
#(#(0, "a"), set.from_list([1])),
#(#(0, "b"), set.from_list([0, 2])),
#(#(1, "a"), set.from_list([1])),
#(#(1, "b"), set.from_list([2])),
// #(#(2, "a"), set.from_list([0])),
// #(#(2, "b"), set.from_list([1])),
])
let init_state = 0
let accepting_states = set.from_list([0, 2])
NFA(states:, alphabet:, transitions:, init_state:, accepting_states:)
} }
pub fn main() { pub fn main() {
io.println("Hello from glemt!") io.println("Hello from glemt!")
let automaton = even_a() let automaton = even_a()
let _ = io.debug(is_dfa(automaton))
io.debug(check_string(automaton, "")) io.debug(check_string(automaton, ""))
io.debug(check_string(automaton, "a")) io.debug(check_string(automaton, "a"))
io.debug(check_string(automaton, "aa")) io.debug(check_string(automaton, "aa"))
io.debug(check_string(automaton, "aaab")) io.debug(check_string(automaton, "aaab"))
let automaton = ends_with_b()
io.debug(is_dfa(automaton))
io.debug(check_string(automaton, ""))
io.debug(check_string(automaton, "a"))
io.debug(check_string(automaton, "aa"))
io.debug(check_string(automaton, "aaaaaa"))
io.debug(check_string(automaton, "aaabaaa"))
io.debug(check_string(automaton, "bbbbbaab"))
io.debug(check_string(automaton, "abaabaaab"))
} }