List Haskell中的无序列表迭代

List Haskell中的无序列表迭代,list,haskell,functional-programming,iteration,fold,List,Haskell,Functional Programming,Iteration,Fold,我正在Haskell实现一个玩具堆叠机。我定义了一个step函数,step::State->Instruction->State,它将给定指令的结果应用于给定状态,并返回机器的结果状态。显然,我希望有一个函数run::State->Program->State,其中Program::[Instruction]可以根据需要多次调用step,以便执行给定的输入程序 我最初天真的解决方案是foldl,如下所示: 运行::状态->程序->状态 运行st prog=foldl步骤st prog 显然,这不

我正在Haskell实现一个玩具堆叠机。我定义了一个step函数,step::State->Instruction->State,它将给定指令的结果应用于给定状态,并返回机器的结果状态。显然,我希望有一个函数run::State->Program->State,其中Program::[Instruction]可以根据需要多次调用step,以便执行给定的输入程序

我最初天真的解决方案是foldl,如下所示:

运行::状态->程序->状态 运行st prog=foldl步骤st prog 显然,这不支持跳转,这将修改我需要在列表中的位置。这个实现所做的只是从左到右遍历程序。对于其他上下文,程序的状态state如下所示:

数据状态=状态{ pc::Word, 注册:单词, 堆栈::[Word], 记忆::[单词] } 衍生秀 具体操作说明如下:

数据操作码=Add | Sub | Mul | Div | Mod | Jump | Push | Pop | Load | Store | Set | Call | Ret | Pos | Dup | Swap | Halt | Nop 导出枚举、显示、等式 数据指令=指令{ 操作码::操作码, arg::也许是单词 } 衍生秀 我如何以任意顺序遍历列表,当然可能是永远遍历,以便支持跳转?

我想您的step函数需要报告是否需要停止。例如,假设您将其类型修改为步骤::状态->指令->可能状态。然后,您可以通过发送到it来实现run:

run :: State -> Program -> State
run state prog = case step state (prog !! fromIntegral (pc state)) of
    Nothing -> state
    Just state' -> run state' prog
你可以通过让你的电脑使用Int而不是Word来避免fromIntegral

注意!!是O2n*在评论中与我较量;-。你应该考虑从[指令]切换到数组字指令,这样你就可以使用!相反,这是开着的

*好吧,准确地说!!从技术上讲是O1,因为Int有一个固定的大小-但是,哦,那个常数因子!所以,让我们说,一个合适的推广!!到整数是O2n。类似的诡辩也适用于

我想您的step函数需要报告是否该停止。例如,假设您将其类型修改为步骤::状态->指令->可能状态。然后,您可以通过发送到it来实现run:

run :: State -> Program -> State
run state prog = case step state (prog !! fromIntegral (pc state)) of
    Nothing -> state
    Just state' -> run state' prog
你可以通过让你的电脑使用Int而不是Word来避免fromIntegral

注意!!是O2n*在评论中与我较量;-。你应该考虑从[指令]切换到数组字指令,这样你就可以使用!相反,这是开着的


*好吧,准确地说!!从技术上讲是O1,因为Int有一个固定的大小-但是,哦,那个常数因子!所以,让我们说,一个合适的推广!!到整数是O2n。类似的诡辩也适用于

使用数组可能是一种很好的方法,但也有一种方法可以逐步完成程序,它更像是一种折叠,完全不用程序计数器

程序计数器只是指向程序中下一步要运行的指令的指针。因此,我们不需要程序计数器,只需将下一步运行的指令置于状态即可。考虑此状态的另一种实现:

数据状态=状态 {reg::Word ,堆栈::[Word] ,内存::[Word] ,program::program }衍生秀 现在我们必须重新考虑这一步骤。特别是,每一步,我们不是增加或随意修改电脑,而是改变要运行的程序。此外,我们不需要将指令作为参数,因为状态已经知道将要运行什么。因此,我们有如下情况:

步骤::状态->状态 步骤st=程序st的案例 []->st 暂停:->st{program=[]} ADD:rest->st{program=rest,…}-也可以为ADD做任何事情 ... 但是,我们要为跳跃做些什么呢?当我们运行程序时,程序正在消失,我们怎么能跳到程序中的任意位置?一种选择是额外跟踪原始程序。我们可以把它作为另一个字段放在State中,但对于variety,我将把它作为额外的参数传递到步骤,如:

步骤::程序->状态->状态 step originalProgram st=案例程序st []->st 暂停:->st{program=[]} 跳转n:->st{program=drop n originalProgram} ... 注意,这里我假设你的跳跃是绝对的,而不是相对的。如果您有相对的跳转,那么您需要跟踪程序中已经执行的部分,而不是跟踪原始程序,可能是作为一个指令列表,以相反的顺序。也就是说,每次执行指令时,都会将其从程序列表中弹出,并将其推送到执行列表中。当你跳转到n条指令的后面时,你只需要从执行的指令中弹出n条,然后把它们推回到程序列表中

现在全部 剩下的就是运行整个程序:

运行::状态->程序->状态 运行startState originalProgram=go startState{program=originalProgram} 哪里 go st=案例程序st []->st _->go$step原始程序st
就性能而言,这肯定比使用阵列更糟糕;大多数步骤都很快,但跳跃可能需要更长的时间。另一方面,您可以通过这种方式运行无限长的程序,而且您不必担心索引超出范围。

使用数组可能是一种很好的方法,但也有一种方法可以逐步完成您的程序,这更像是一种折叠式的方法,即完全取消程序计数器

程序计数器只是指向程序中下一步要运行的指令的指针。因此,我们不需要程序计数器,只需将下一步运行的指令置于状态即可。考虑此状态的另一种实现:

数据状态=状态 {reg::Word ,堆栈::[Word] ,内存::[Word] ,program::program }衍生秀 现在我们必须重新考虑这一步骤。特别是,每一步,我们不是增加或随意修改电脑,而是改变要运行的程序。此外,我们不需要将指令作为参数,因为状态已经知道将要运行什么。因此,我们有如下情况:

步骤::状态->状态 步骤st=程序st的案例 []->st 暂停:->st{program=[]} ADD:rest->st{program=rest,…}-也可以为ADD做任何事情 ... 但是,我们要为跳跃做些什么呢?当我们运行程序时,程序正在消失,我们怎么能跳到程序中的任意位置?一种选择是额外跟踪原始程序。我们可以把它作为另一个字段放在State中,但对于variety,我将把它作为额外的参数传递到步骤,如:

步骤::程序->状态->状态 step originalProgram st=案例程序st []->st 暂停:->st{program=[]} 跳转n:->st{program=drop n originalProgram} ... 注意,这里我假设你的跳跃是绝对的,而不是相对的。如果您有相对的跳转,那么您需要跟踪程序中已经执行的部分,而不是跟踪原始程序,可能是作为一个指令列表,以相反的顺序。也就是说,每次执行指令时,都会将其从程序列表中弹出,并将其推送到执行列表中。当你跳转到n条指令的后面时,你只需要从执行的指令中弹出n条,然后把它们推回到程序列表中

现在剩下的就是运行整个程序:

运行::状态->程序->状态 运行startState originalProgram=go startState{program=originalProgram} 哪里 go st=案例程序st []->st _->go$step原始程序st
就性能而言,这肯定比使用阵列更糟糕;大多数步骤都很快,但跳跃可能需要更长的时间。另一方面,您可以通过这种方式运行无限长的程序,并且不必担心索引超出范围。

作为补充说明,由于Haskell的标准库中已经存在状态类型,所以您可能不应该将其称为State。现在似乎是使用显式递归而不是依赖foldl之类的东西的好时机。如果程序不应该停止,则返回步骤的结果,否则返回完成状态。你可能会发现,由于链表的性质,跳转速度非常慢,所以现在可能是跳转的好时机。你有一台电脑,但你正在使用折叠-这似乎有点不合适,不是吗?如果你想使用个人电脑,那么你可能想要一个指令向量,你可以索引到它,而不是一个列表。如果您想使用列表,请完全废弃pc并将程序存储在状态。诀窍在于:存储当前执行、尚未运行的指令列表和整个程序。然后,处理跳转成为可能。@DDub这是问题的关键,是的。我需要显式递归来实现跳跃本身,我只是在制定它时遇到了麻烦。值得一提的是,我有一个等效的Rust实现,但它使用可变状态来跟踪程序代码中的当前位置。@Aplet123感谢状态警告-我假设Haskell通过其模块系统理智地管理它?否则,我将更改名称。作为补充说明,您可能不应该将其称为State,因为Haskell的标准库中已经存在状态类型。现在似乎是使用显式递归而不是依赖foldl之类的东西的好时机。如果程序不应该停止,则返回步骤的结果,否则返回完成状态。你可能会发现,由于链表的性质,跳转速度非常慢,所以这可能是一个很好的时机。你有一台电脑,但你正在使用折页-这似乎是一个很好的选择
一点点,不是吗?如果你想使用个人电脑,那么你可能想要一个指令向量,你可以索引到它,而不是一个列表。如果您想使用列表,请完全废弃pc并将程序存储在状态。诀窍在于:存储当前执行、尚未运行的指令列表和整个程序。然后,处理跳转成为可能。@DDub这是问题的关键,是的。我需要显式递归来实现跳跃本身,我只是在制定它时遇到了麻烦。值得一提的是,我有一个等效的Rust实现,但它使用可变状态来跟踪程序代码中的当前位置。@Aplet123感谢状态警告-我假设Haskell通过其模块系统理智地管理它?否则我将更改名称。我猜+@Integer也处于启用状态,然后:-P@chi是的!现在,如果你知道什么是最好的边界,让我知道…我猜它只是一个很好的例子,但是它的证明太长了,不能用在这个评论中™.将此标记为答案,因为它直接解决了原始问题。我猜+@Integer也处于启用状态,然后:-P@chi是的!现在,如果你知道什么是最好的边界,让我知道…我猜它只是一个很好的例子,但是它的证明太长了,不能用在这个评论中™.将此标记为答案,因为它直接解决了原始问题。我没有想过以这种方式重新设计它-这非常优雅。我没有想过以这种方式重新设计它-这非常优雅。