【问题标题】:how to simulate haskell state?如何模拟haskell状态?
【发布时间】:2013-03-12 08:48:01
【问题描述】:

我编写了一些 haskell 代码来切换树莓派上的一个引脚,这取决于我从树莓派上的另一个引脚获得的中断。在不知道先前的切换状态的情况下,我根本不知道如何切换引脚的状态。程序本身非常简单。

 import Control.Concurrent
 import Data.IORef
 import HasberryPi


 main = do wiringPiSetup
           pinMode 0 output
           pinMode 7 input 
           pullUpDnControl 7 pull_down
           wiringPiISR 7 edge_both onoff
           threadDelay (15*(10^6))

 onoff s = do a <- readIORef s                               -- This is wrong
              digitalWrite 0 (if b then pinhigh else pinlow) -- This is wrong

所以基本上这里发生的是引脚 7 被注册为中断。只要引脚 7 从高电平变为低电平或从低电平变为高电平,就会触发中断。并且每当触发中断时,它都会调用onoff 函数来切换引脚0 的状态。

main 函数是正确的。它的onoff 函数就是问题所在。 onoff 函数的预期行为是在引脚 0 为低电平时使引脚 0 为高电平,并在引脚为高电平时将引脚切换为低电平。但要做到这一点,我需要在上一次对onoff 的调用中存储引脚的先前状态。

我尝试了状态单子。但问题是状态单子基于初始状态值传递状态。但在随后对onoff 的调用中,似乎不可能更改初始状态值。我想到了 IORef ,似乎没有什么不同。看起来它正在做状态正在做的事情..但仅限于 IO 内部。

我可以清楚地看到我非常缺少将状态存储在全局变量中的能力。而且我很高兴我无法做到这一点,因为我知道还有其他一些惯用的方法可以实现相同的目标。

非常感谢任何正确方向的帮助。

干杯和问候。

【问题讨论】:

  • 程序开始时pin 0是高还是低?你怎么知道的?如果你能回答这个问题,那么你就可以使用 State。如果没有办法知道,我想您需要在开始时自己将其设置为高或低并相应地初始化 State。
  • pin 0 将在程序开始时为低电平

标签: haskell state


【解决方案1】:

State monad 实际上是对在函数的额外参数中传递状态的想法的抽象——它仍然是纯粹的,它只是为你提供了很多语法帮助。另一方面,IORef 是实际可变值的实际更新,这就是它必须存在于 IO monad 中的原因。这通常被认为是不可取的,除非出于性能原因需要,因为您失去了使用纯代码获得的所有关于惰性、执行顺序和并发性的承诺。

同时使用StateIO 是通过使用StateT monad 转换器实现的,这可能被认为是将State monad 包裹在IO monad 周围。 Haskell wiki 上有一些示例:http://www.haskell.org/haskellwiki/Simple_StateT_use,它展示了在使用 I/O 时如何维护状态,以及如何使用 lift 让 IO monad 函数在 StateT 中运行。

【讨论】:

  • 根据HasberryPi中的API,onoff的类型必须是IO()。不可能是别的。所以我看不到如何在代码中使用 state monad。
  • 这就是你使用StateT的原因,它是一个monad转换器,可以将状态添加到任何其他monad。然后,您可以使用lift 调用IO 中的任何内容,这需要(例如)a -&gt; IO () 类型的内容并将其转换为a -&gt; StateT MyState IO () 之类的内容。如果我链接的示例不清楚,您需要阅读 monad 转换器,特别是 StateT 转换器,可能还有一般的 monad。 en.wikibooks.org/wiki/Haskell/Monad_transformers 对我来说似乎很清楚,但是当您已经了解材料时,很难判断教程。
【解决方案2】:

这是一个小例子。我不确定它是否是惯用的 Haskell,但它应该足以让你走上正轨。而不是实际切换一个引脚(我没有要测试的 Raspberry Pi),它只是打印状态。它们都是 IO () ,所以应该匹配。

你的真实状态大概是一个记录/列表/引脚数组。然后,您将一个索引传递给 togglePin,它的类型类似于

togglePin :: Int -> PStateT

无论如何 - 这是示例,它在这里编译并运行良好。

import Control.Monad.State

-- Presumably you've got something like this defined in a library
data Pin = PinHigh | PinLow
    deriving (Eq,Show)

-- A simple state would be
-- type PState = State Pin
-- We want to wrap our state around IO () so need a transformer
type PStateT = StateT Pin IO ()

-- Simple print function
printPinState :: String -> Pin -> IO ()
printPinState msg pin = putStrLn $ msg ++ (show pin)

-- Toggles the state, real function would set the pin's level too rather than 
-- just print it's new state
togglePin :: PStateT
togglePin = do
    curr_p <- get
    lift $ printPinState "toggle before: " curr_p
    let new_p = if curr_p == PinHigh then PinLow else PinHigh
    lift $ printPinState "toggle after:  " new_p
    put new_p
    return ()

-- Initialise our state, then run our toggle function using the state
-- as its environment.
main = do
    let env = PinLow
    printPinState "main before:   " env
    (_, env') <- runStateT (togglePin) env
    printPinState "main after:    " env'
    -- And again for luck...
    (_, env'') <- runStateT (togglePin) env'
    return ()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-25
    • 2012-08-15
    • 1970-01-01
    • 2013-07-14
    • 1970-01-01
    • 2017-04-06
    • 2011-04-26
    • 2020-08-28
    相关资源
    最近更新 更多