如何计算数据类型传递给函数的次数和总值?我是FP的新手,不确定这是否是可变性法律或参考透明度允许的。上下文正在使用堆栈并尝试解决,如果您将一系列指令传递给堆栈,您可以计算出传入特定指令的频率以及所有这些类型的总值,作为一种计数器......我已经四处寻找无济于事并开始认为我的方法可能存在根本性的缺陷,所以任何建议都会受到赞赏,但我想我会把它放在那里,因为我有兴趣知道,我正在按照以下方式工作; < / p>
> data Value > = Numeric Int > | Logical Bool > deriving (Eq, Show, Read) ... > data Instruction > = Push Value > | Pop > | Fetch Int > | Store Int ... > step inst c= > case (inst) of > (Push, stack) -> (c', x : stack) > (Pop, _ : stack) -> (c', stack) > where > c = c' + 1 ...
答案 0 :(得分:6)
您可以使用State
中的Control.Monad.State
monad,而不是显式管理堆栈。有关内部工作的详细信息,请阅读文档。
step :: Instruction -> State [Value] ()
step (Push v) = do
stack <- get
put (v:stack)
step Pop = do
(_:stack) <- get
put stack
您还可以存储状态中每条指令的编号:
step :: Instruction -> State (Int, Int, Int, Int, [Value]) ()
step (Push v) = do
(a, b, c, d, stack) <- get
put (a+1, b, c, d, v:stack)
step Pop = do
(a, b, c, d, (_:stack)) <- get
put (a, b+1, c, d, stack)
使用5元组有点麻烦,因此您可能需要为此定义自己的数据类型。在此模型中,第一个Int
是Push
es的数量,第二个是Pop
s的数量等。
答案 1 :(得分:2)
因此,您需要应用整个堆栈操作序列,并获得调用统计信息的结果堆栈和操作。为了以纯粹的方式积累它们,您需要将它们与您的操作链一起携带。实际上,您可以通过某些方式实现这一目标:
1)明确地将stats添加到每个函数调用并手动组合;
2)或将它们包装成monad,以便可以使用>>=
或sequence
自动链接调用。
最后一个提出了一些特殊的变体。
2.1)使用前面提出的 user2407038 State
。它隐藏了一个额外的参数,它携带统计数据,因此它应该看起来像一个命令状态,可以通过put,get和modify进行操作。
2.2)使用Writer
,这可以被认为是一个“无脂肪”状态,你只能在你的携带统计数据中添加«某些东西»(例如,哪个操作被调用) - 这实际上就是你所需要的(如我能够了解)。计算会更简单,因为代替所有put
- s,get
- s和modify
- es,您将拥有单个tell
。但是,您需要将Stats
类型设为Monoid
的实例(尽管这很容易和线性)。
2.3)使用ST
,类型可能非常可怕,但您可以使用可变侵权计数器来提高性能。但是,如果没有必要,我不建议这样做。
答案 2 :(得分:1)
您可以为每条指令分配一个数字,并为该函数添加一个额外的参数,以便当数字和指令匹配时,计数会增加。输入和输出将是程序,堆栈和计数器。
step i1 (push x : insts, stack, c) = if i1 == 0 then step i1 (insts, x : stack, c + 1) else step i1 (insts, x : stack, c)
step i1 (pop : insts, _ : stack, c) = if i1 == 1 then step i1 (insts, stack, c + 1) else step i1 (insts, stack, c)