#自动机 #状态 #执行 #机制 #编程 #连接 #控制

automata-like-programming

提供模仿自动机控制执行流程机制的库

1 个不稳定版本

0.1.0 2024年8月11日

算法 中排名 561

Download history 44/week @ 2024-08-05 83/week @ 2024-08-12

每月下载量 127

MIT 许可协议 MIT

32KB
286

crates.io

自动机编程

自动机是一种自动执行一系列操作的机器。它可以由一个状态图来描述,其中状态通过标记有输入符号的边连接,这些符号将使自动机跳转到连接的状态。此库通过使用简单实现(模块 simple_impl)或手动实现状态,提供了一种简单的流程来实施此类自动机。当没有更多的连接可用或输入结束时,自动机停止执行。在整个运行过程中,自动机使用相同的可变数据引用,并可以在状态转换期间执行自定义操作(操作取决于状态实现)。如果状态跳转期间发生错误,自动机可以带有错误退出。

将预定义字符串连接成 "FooBar" 的示例自动机

此自动机具有预定义的执行序列(没有输入可以控制执行状态的顺序)。这意味着它将一直运行到达到最后一个状态(顺序执行)。

use std::rc::Rc;
use automata_like_programming::{
        automaton::AutomatonResult, automaton_state::{
            new_shared_automaton_state, AutomatonState, SharedAutomatonState
           }
    };
use automata_like_programming::automaton::{Automaton, NextState};
// Example implementation of an automaton state that appends specified text into
// mutable buffer.
pub struct TestState {
    id: u8,
    text: &'static str,
    next_state: Option<SharedAutomatonState<'static, u8, String, String>>
}

impl TestState {
    pub fn new(
        id: u8, 
        text: &'static str, 
        next_state: Option<SharedAutomatonState<'static, u8, String, String>>
    ) -> Self {
        Self { id, text, next_state }
    }
}

impl AutomatonState<'static, u8, String, String> for TestState {
    fn get_id_owned(
        &self
    ) -> u8 {
        self.id
    }
    
    fn get_id(
        &self
    ) -> &u8 {
        &self.id
    }
    
    fn execute_next_connection(
        &self, 
        data: &mut String
    ) -> Result<NextState<'static, u8, String, String>, String> {
        data.push_str(self.text);
        if let Option::Some(nxt_state) = &self.next_state {
            Result::Ok(NextState::Continue(Rc::clone(nxt_state)))
        } else {
            Result::Ok(NextState::NotFound)
        }
    }
}

let mut automaton = Automaton::new(|| {
    // First we create the "Bar" state as it's the last state and it doesn't connect to
    // any other state.
    let bar_state = new_shared_automaton_state(
                        TestState::new(2, "Bar", Option::None)
                    );
    // Secondly we declare the "Foo" state which is connected to "Bar" state.
    let foo_state = new_shared_automaton_state(
                        TestState::new(1, "Foo", Option::Some(Rc::clone(&bar_state)))
                    );
    foo_state
});
let mut buffer = String::with_capacity(6);
let result = automaton.run(&mut buffer);
assert!(result.is_could_not_find_next_state());
assert_eq!("FooBar", buffer);

简单状态实现的示例模式匹配

此包提供了一个自动机状态的简单实现。它使用传递给自动机的数据提供的键(例如,数据包含迭代器)。每个状态都有连接,这些连接定义了与给定键匹配的进程、在匹配此连接时要执行的进程以及将变得活动的下一个状态。以下是一个查找给定字符串中 "ab" 模式的自动机的示例。

use automata_like_programming::{
        automaton::{
            Automaton,
            AutomatonResult
        },    
        automaton_state::new_shared_concrete_state,
        simple_impl::simple_state::{
            KeyProvidingData,
            SimpleInterStateConnection,
            SimpleStateImplementation
        }
    };

 struct TextMatching<'a> {
    text: &'a str,
    matches: Vec<usize>,
    iter: usize,
}

impl <'a> TextMatching<'a> {
    pub fn new(
        text: &'a str
    ) -> Self {
        Self { text, matches: Vec::new(), iter: 0}
    }
    pub fn add_match(
        &mut self, index: usize
    ) -> () {
        self.matches.push(index);
    }
}

impl <'a> KeyProvidingData<(usize, char)> for TextMatching<'a> {
    fn next_key(
        &mut self
    ) -> Option<(usize, char)> {
        if self.iter >= self.text.len() {
            Option::None
        } else {
            self.iter += 1;
            Option::Some((self.iter - 1, self.text.chars().nth(self.iter)?))
        }
    }
}

fn char_matcher(
    c: char,
    reversed: bool
) -> impl Fn(&(usize, char)) -> bool {
    move |k| (k.1 == c) ^ reversed
}

let mut matching_data = TextMatching::new("aabbacacaabab");
let mut automaton: Automaton<u32, TextMatching, String> = Automaton::new(|| {
    let non_match_state = new_shared_concrete_state(SimpleStateImplementation::new(0));
    non_match_state.borrow_mut().register_connection(
        SimpleInterStateConnection::new_no_action(char_matcher('a', true), &non_match_state)
    );
    let a_state = new_shared_concrete_state(SimpleStateImplementation::new(1));
    non_match_state.borrow_mut().register_connection(
        SimpleInterStateConnection::new_no_action(char_matcher('a', false), &a_state)
    );
    a_state.borrow_mut().register_connection(
        SimpleInterStateConnection::new_no_action(char_matcher('a', false), &a_state)
    );
    a_state.borrow_mut().register_connection(
        SimpleInterStateConnection::new_no_action(char_matcher('b', true), &non_match_state)
    );
    
    let b_state = new_shared_concrete_state(SimpleStateImplementation::new(2));
    a_state.borrow_mut().register_connection(
        SimpleInterStateConnection::new(char_matcher('b', false),
        |data: &mut TextMatching, key| {
            data.add_match(key.0);
            Result::Ok(())
        }, &b_state)
    );
    b_state.borrow_mut().register_connection(
        SimpleInterStateConnection::new_no_action(char_matcher('a', false), &a_state)
    );
    b_state.borrow_mut().register_connection(
        SimpleInterStateConnection::new_no_action(char_matcher('a', true), &non_match_state)
    );
    non_match_state
});
let result = automaton.run(&mut matching_data);
assert!(result.is_empty_iter());
assert_eq!(matching_data.matches, vec![1, 9, 11]);

无运行时依赖项