pub struct Solution;
Expand description
用有限状态机,处理几个有限的状态就解决了。
Rust 的好处是很容易写比较高级的抽象。而且对错误处理非常友好。语法上主要是 Pattern Matching 和各种高阶函数的 combinator 组合。感觉用 Erlang/Elixir 写起来应该会很方便。
代码逻辑:
chars()
生成字符串的Iterator
try_fold
消费这个Iterator
,创建一个新的Validator
对象作为 fold 的初始状态传入。try_fold
的每一次回调处理一个字符,调用validator.handle(c)
返回一个Result
。 只要每步都 Ok,那就一直 handle 下去,只要中间发现 Err,就会提早返回 (短路操作)- 最后消费完
Iterator
也就是所有字符都处理完了,需要判断下Validator.is_end()
是否合法结束,即检查Validator
的状态是否是End
(完全闭合最外层的标签)。
主要是 Validator
里的几个状态定义:
Init
初始状态,只接收<
就进入TagName
状态,否则失败TagName
这个状态比较复杂,其实就是 Tag 的定义条件,这里用了一个is_close
来标识是不是 关闭标签TagContent
这个状态简单,只要遇到<
就进入TagName
状态,其它都 OkCDataTag
这个也只有一种情况,读取完整的[CDATA[
进入CDataContent
状态,否则失败CDataContent
这个状态也简单(用了两个bool
标记是否已经有连续两个]
),遇到]]>
就结束,回到TagContent
状态(注意 reset 标记)End
最外层的 Tag 闭合,结束,不能再有内容(即如果这个状态下再接收到内容,都是 Err)
这里就 TagName
里处理得最多,具体条件结合题目定义,看代码。
另外,Validator
里定义了一个 stack
,记录 开标签,用于与 闭标签 匹配。
Rust 的类型系统使得错误处理机制非常严谨,安全。代码里每一个地方都记录了 invalid 的错误信息,可以在最外层调用的地方统一打印。
因为用 Iterator
,是 lazy 调用,只有在每一步迭代的时候才会执行代码,只迭代了一遍。(如果要调试输入的情况,在 try_fold
前面加一个 inspect
可以打印出每个字符,结合每个地方的状态变化,可以很轻松地调试)。
因为代码抽象程度比较好,虽然看起来代码长了一点,但结构非常清晰,可读性,安全性,可扩展/维护性都很优秀。
Implementations§
Auto Trait Implementations§
impl Freeze for Solution
impl RefUnwindSafe for Solution
impl Send for Solution
impl Sync for Solution
impl Unpin for Solution
impl UnwindSafe for Solution
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more