Ragel 转换动作和状态动作之间的区别
Difference between Ragel transition actions and state actions
状态机、术语和工具对我来说都是新的,尽管我最近一直在尝试使用各种在线资源来了解它们。这开始于我想在 Ragel 和 Go 中构建一个比正则表达式更快的解析器。我对 Ragel docs 的第 3 章感到困惑,其中涵盖了操作。
我不清楚与状态转换相关的操作与状态本身之间的区别。这些示例仅对状态嵌入操作有错误,因此我不确定您何时会使用 to
和 from
运算符。我做了一个简单的例子:
package main
import (
"fmt"
)
%% machine scanner;
%%{
action fooStart { fmt.Println("foo start")}
action fooEnd { fmt.Println("foo end")}
action barStart { fmt.Println("bar start")}
action barEnd { fmt.Println("bar end")}
action bazStart { fmt.Println("baz start")}
action bazEnd { fmt.Println("baz end")}
main := "foo" >fooStart @fooEnd "bar" >barStart @barEnd "baz" >bazStart @bazEnd;
}%%
%% write data;
func main() {
ParseEmbedding([]byte("foobarbaz"))
}
func ParseEmbedding(data []byte) {
cs, p, pe := 0, 0, len(data)
%%{
write init;
write exec;
}%%
}
输出以下内容,这是我所期望的:
foo start
foo end
bar start
bar end
baz start
baz end
但是,如果我使用相同的周围代码将它们替换为状态嵌入操作:
main := "foo" >~fooStart %*fooEnd "bar" >~barStart %*barEnd "baz" >~bazStart %*bazEnd;
我得到以下输出,其中的顺序和缺失的行对我来说没有意义:
bar start
foo end
baz start
bar end
我的初步印象是,当您只想 运行 整个状态机中某些点的动作而不是更细粒度的转换时,会使用状态嵌入式动作。这仍然让我对它们的执行顺序感到困惑,以及为什么不执行 "foo" 的开始 "to-state" 操作。
所以我的问题是,使用状态操作而不是转换操作的一些实际原因或示例是什么?通俗地说,它们的工作方式有何不同?
这是状态嵌入操作的图表。
通常您想使用过渡动作。我实际上很少使用基于状态的操作。大多数情况下,只有当我想初始化一些东西并且我希望它在查看角色之前执行时。例如,在执行条件之前。目前,基于状态的行动是实现这一目标的唯一途径。
有一段时间我梦想有一个解决方案,您可以在测试条件之前嵌入 运行 基于转换的操作,这样您就不需要 init 的基于状态的操作。使用基于状态的操作总是感觉有点像黑客。
状态机、术语和工具对我来说都是新的,尽管我最近一直在尝试使用各种在线资源来了解它们。这开始于我想在 Ragel 和 Go 中构建一个比正则表达式更快的解析器。我对 Ragel docs 的第 3 章感到困惑,其中涵盖了操作。
我不清楚与状态转换相关的操作与状态本身之间的区别。这些示例仅对状态嵌入操作有错误,因此我不确定您何时会使用 to
和 from
运算符。我做了一个简单的例子:
package main
import (
"fmt"
)
%% machine scanner;
%%{
action fooStart { fmt.Println("foo start")}
action fooEnd { fmt.Println("foo end")}
action barStart { fmt.Println("bar start")}
action barEnd { fmt.Println("bar end")}
action bazStart { fmt.Println("baz start")}
action bazEnd { fmt.Println("baz end")}
main := "foo" >fooStart @fooEnd "bar" >barStart @barEnd "baz" >bazStart @bazEnd;
}%%
%% write data;
func main() {
ParseEmbedding([]byte("foobarbaz"))
}
func ParseEmbedding(data []byte) {
cs, p, pe := 0, 0, len(data)
%%{
write init;
write exec;
}%%
}
输出以下内容,这是我所期望的:
foo start
foo end
bar start
bar end
baz start
baz end
但是,如果我使用相同的周围代码将它们替换为状态嵌入操作:
main := "foo" >~fooStart %*fooEnd "bar" >~barStart %*barEnd "baz" >~bazStart %*bazEnd;
我得到以下输出,其中的顺序和缺失的行对我来说没有意义:
bar start
foo end
baz start
bar end
我的初步印象是,当您只想 运行 整个状态机中某些点的动作而不是更细粒度的转换时,会使用状态嵌入式动作。这仍然让我对它们的执行顺序感到困惑,以及为什么不执行 "foo" 的开始 "to-state" 操作。
所以我的问题是,使用状态操作而不是转换操作的一些实际原因或示例是什么?通俗地说,它们的工作方式有何不同?
这是状态嵌入操作的图表。
通常您想使用过渡动作。我实际上很少使用基于状态的操作。大多数情况下,只有当我想初始化一些东西并且我希望它在查看角色之前执行时。例如,在执行条件之前。目前,基于状态的行动是实现这一目标的唯一途径。
有一段时间我梦想有一个解决方案,您可以在测试条件之前嵌入 运行 基于转换的操作,这样您就不需要 init 的基于状态的操作。使用基于状态的操作总是感觉有点像黑客。