2013-03-12 54 views
4

我寫了一些haskell代碼來切換樹莓派上的一個引腳,這取決於我從樹莓派上另一個引腳獲得的中斷。我只是不知道如何在不知道以前的切換狀態的情況下切換引腳的狀態。程序本身非常簡單。如何模擬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

我試過了狀態monad。但問題在於狀態monad基於初始狀態值傳遞狀態。但在隨後調用onoff似乎不可能改變初始狀態值。我想到了IORef,似乎沒有什麼不同。它看起來像是在做什麼狀態......但只在IO內部。

我可以清楚地看到,我非常想念在全局變量中存儲狀態的能力。我很高興我無法做到這一點,因爲我知道還有其他一些實現相同目標的慣用方式。

任何幫助正確的方向非常感謝。

乾杯和問候。

+0

引腳0在程序開始時爲高電平還是低電平?你怎麼知道的?如果你可以回答,那麼你可以使用狀態。如果沒有辦法知道,我想你應該在啓動時自己設置高或低,並相應地初始化狀態。 – 2013-03-12 09:08:20

+0

程序開始時引腳0將爲低電平 – Jay 2013-03-12 09:18:53

回答

6

State monad實際上是一個抽象概念,將狀態傳遞給函數的額外參數 - 它仍然是純粹的,它只是爲您提供了大量的語法幫助。另一方面,IORef是實際可變值的實際更新,這就是爲什麼它必須在IO monad中生存。這通常被認爲是不受歡迎的,除非出於性能原因需要,因爲您失去了所有關於懶惰和執行順序以及併發性的純代碼的承諾。

使用StateIO一起是通過使用StateT monad變壓器,可能被認爲是圍繞IO monad包裝狀態monad實現的。 Haskell wiki上有一些示例:http://www.haskell.org/haskellwiki/Simple_StateT_use,其中顯示瞭如何在使用I/O時保持狀態,以及如何使用lift獲得IO monad函數在StateT內運行。

+0

根據HasberryPi中的API,onoff的類型必須爲IO()。它不能是別的。所以我看不到我如何在代碼中使用狀態monad。 – Jay 2013-03-12 13:25:30

+0

這就是爲什麼你使用'StateT',這是一個monad轉換器,它將狀態添加到任何其他monad。然後你可以使用'lift'來調用'IO'中的任何東西,'lift'包含(例如)'a - > IO()'類型的東西,並將它變成類似'a - > StateT MyState IO()'的東西。如果我鏈接的例子不清楚,那麼您需要詳細閱讀monad變換器和特定的'StateT'變壓器,以及一般可能的monad。 http://en.wikibooks.org/wiki/Haskell/Monad_transformers對我來說似乎很清楚,但是當你已經知道這些材料時很難判斷教程。 – 2013-03-12 14:18:18

0

這是一個小例子。我不確定它是否是慣用的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()