{:check ["true"]}

Index

Automaton Recognizer

Automaton based string matcher

Define the data types for

  • State
  • Table
In [3]:
typealias State = String
typealias Table = HashSet<Triple<State, State, Char?>>

// Kotlin feature: extension function
fun Table.add(source: State, target: State, label: Char?): Table {
    this.add(Triple(source, target, label))
    return this
}

Define the automaton class

$$A = \left<S, s_0, F, \Delta\right>$$
In [4]:
class Automaton (
    val S: Set<State>,
    val s0: State,
    val F: Set<State>,
    val Δ: Table,
)

Consider the following automaton:

image.png

In [7]:
val states = setOf("1", "2", "3")
val finals = setOf("1")
val initial = "1"
val transitions = Table()
    .add("1", "2", 'b')
    .add("1", "3", null)
    .add("2", "2", 'a')
    .add("2", "3", 'a')
    .add("2", "3", 'b')
    .add("3", "1", 'a')
    
val automaton = Automaton(states, initial, finals, transitions)

Algorithms are methods of Automaton

Define the closure function, which computes all reachable states of $T\subset S$ via only the epsilon transitions.

In [8]:
// Closure is an extension function of Automaton.
fun Automaton.closure(T: Iterable<State>): Set<State> {
    val result = mutableSetOf<State>().apply {
        addAll(T)
    }

    do {
        val newResultStates = this.Δ.filter {
            triple ->
            val (source, target, label) = triple
            ((source in result) && 
                    (label == null) && 
                    !(target in result))
        }.map {
            triple -> triple.second
        }
        
        result.addAll(newResultStates)
    } while(newResultStates.isNotEmpty())
    
    return result
}
In [10]:
// Do a simple test
automaton.closure(listOf("1"))
Out[10]:
[1, 3]

Define the reachable method which computes all reachable states from $T\subset S$ via a string $x\in\Sigma^*$.

In [11]:
fun Automaton.reachable(T: Iterable<State>, x: String): Set<State> {
    var result:Set<State> = this.closure(T)
    for(c:Char in x) {
        result = this.Δ.filter {
            (it.first in result) && it.third == c
        }.map {
            it.second
        }.toSet()
        
        result = this.closure(result)
    }
    
    return result
}
In [17]:
println("ba: " + automaton.reachable(setOf(automaton.s0), "ba"))
println("bb: " + automaton.reachable(setOf(automaton.s0), "bb"))
println("bbc: " + automaton.reachable(setOf(automaton.s0), "bbc"))
ba: [3, 2]
bb: [3]
bbc: []

Finally, now we can implement matches which accepts a string, and returns true of the automaton accepts the string.

In [18]:
fun Automaton.matches(x: String): Boolean =
    this.reachable(listOf(s0), x).intersect(F).isNotEmpty()
In [19]:
val strings = listOf(
    "aabb",
    "ab",
    "aaaaaaa",
    "bba",
    "bbb",
)

for(x in strings) {
    println("${x}: ${automaton.matches(x)}")
}
aabb: false
ab: false
aaaaaaa: true
bba: true
bbb: false
In [ ]: