【发布时间】:2021-08-06 07:22:56
【问题描述】:
我正在 Haskell 中实现一个(玩具)堆栈机器。我定义了一个步进函数step :: State -> Instruction -> State,它将给定指令的结果应用于给定状态并返回机器的结果状态。显然,我想要一个函数,run :: State -> Program -> State(其中Program :: [Instruction]),它基本上根据需要多次调用step,以便执行给定的输入程序。
我最初的天真的解决方案是foldl,就像这样:
run :: State -> Program -> State
run st prog = foldl (step) st prog
显然,这不支持跳转,这会修改我需要在列表中的位置。这个实现所做的就是从左到右遍历程序。对于其他上下文,程序的状态 State 如下:
data State = State {
pc :: Word,
reg :: Word,
stack :: [Word],
memory :: [Word]
}
deriving (Show, Eq)
说明如下:
data Opcode = Add | Sub | Mul | Div | Mod | Jump | Push | Pop | Load | Store | Set | Call | Ret | Pos | Dup | Swap | Halt | Nop
deriving (Enum, Show, Eq)
data Instruction = Instruction {
opcode :: Opcode,
arg :: Maybe Word
}
deriving (Show, Eq)
如何以任意顺序(当然也可能永远)遍历列表,以便支持跳转?
【问题讨论】:
-
附带说明,您可能不应该将其称为
State,因为 Haskell 的标准库中已经存在State类型。 -
这似乎是使用显式递归而不是依赖于
foldl之类的东西的好时机。如果程序不应该停止,则使用step的结果进行递归,否则返回完成状态。由于链表的性质,您可能会发现跳转非常缓慢,因此这可能是look into arrays 的好时机。 -
你有一个
pc但你正在使用折叠——这似乎有点不对劲,不是吗?如果你想使用一台电脑,那么你可能想要一个指令向量之类的东西,你可以索引到而不是一个列表。如果您更愿意使用该列表,请将 PC 完全报废并将程序存储在State中。这是诀窍:存储当前执行(尚未运行的指令列表)和“整个程序”。然后,处理Jump成为可能。 -
@DDub 这是问题的症结所在,是的。我需要显式递归来启用“跳跃”本身,我只是在制定它时遇到了麻烦。值得一提的是,我有一个等效的 Rust 实现,但它使用可变状态来跟踪程序代码中的当前位置。
-
@Aplet123 感谢
State的警告——我假设Haskell 用它的模块系统管理得很好?不然我就改名字了。
标签: list haskell functional-programming iteration fold