5

我写了一些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 为高电平,并在引脚为高电平时将引脚切换为低电平。但要做到这一点,我需要在上一次调用onoff.

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

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

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

欢呼和问候。

4

2 回答 2

7

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

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

于 2013-03-12T09:07:08.880 回答
1

这是一个小例子。我不确定它是否是惯用的 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 ()
于 2013-03-12T17:58:49.467 回答