2013-10-21 52 views
3

我有一個應該接受STUArray,修改一些元素,然後返回改變陣列小哈斯克爾功能工作。它將從另一個在ST s (STUArray s Int Word32) monad中工作的函數調用。它是我試圖編寫的一個快速PBKDF2函數的一部分。該函數爲固定大小的消息(160位)執行SHA-1填充。哈斯克爾功能與STUArray

這是我的代碼:

padFixed :: STUArray s Int Word32 -> ST s (STUArray s Int Word32) 
padFixed block = do 
    unsafeWrite block 5 0x80000000 
    unsafeWrite block 15 160 
    return block 

陣列將包含從先前的SHA-1運行的20個字節,加上44個字節的零。它將根據RFC 3174添加所需的填充。

我該如何重寫它,以便將數組從「monad」中取出,對其進行處理,然後將其放回原處?簽名應該是padFixed :: ST s (STUArray s Int Word32),沒有block參數。

這可能嗎?我無法在庫中找到任何讓我從monad中提取數組的函數,但也許我錯過了一些東西。

STArray有什麼好的教程嗎?

回答

4

不,這是不可能的; ST沒有這些語義。單子是ST s,而不是ST s (STUArray s a)ST s只是一個用於跟蹤可變狀態的monad;您選擇在單個ST區域內分配和使用的結構由您決定。如果你有一大堆的計算,所有在同一STUArray操作,您可以使用ReaderT

type Hasher s = ReaderT (STUArray s Int Word32) (ST s) 

padFixed :: Hasher() 
padFixed = do 
    block <- ask 
    unsafeWrite block 5 0x80000000 
    unsafeWrite block 15 160 

Reader r單子只是一個圍繞r ->包裝;類型Reader r a的值只是一個函數r -> a。這實際上是一種計算a,同時可以訪問類型r的值的方法。 ReaderT r monad變換器只允許您提供對r類型的變量的訪問以進行任意一次性計算;因此,ReaderT (STUArray s Int Word32) (ST s)是一個ST s計算,它可以訪問某個數組。請注意,您不需要從padFixed返回數組; monad綁定將處理所有這些。

這將是一個痛苦的一點點寫的,因爲我們必須要保持ask荷蘭國際集團的陣列。幸運的是,我們可以寫一些組合子來處理這個我們:

{-# LANGUAGE RankNTypes, GeneralizedNewtypeDeriving #-} 

import Data.Word 
import Control.Applicative 
import Control.Monad.Reader 
import Control.Monad.ST 
import Data.Array.ST (STUArray, runSTUArray) 
import qualified Data.Array.Base as A 
import Data.Array.Unboxed (UArray) 

newtype Hasher s a = 
    Hasher { getHasher :: ReaderT (STUArray s Int Word32) (ST s) a } 
    deriving (Functor, Applicative, Monad, MonadReader (A.STUArray s Int Word32)) 

hasherToST :: Hasher s() -> (Int,Int) -> ST s (STUArray s Int Word32) 
hasherToST (Hasher r) bounds = do 
    block <- A.newArray bounds 0 
    runReaderT r block 
    return block 

runHasher :: (forall s. Hasher s()) -> (Int,Int) -> UArray Int Word32 
runHasher h bounds = runSTUArray $ hasherToST h bounds 

-- Perhaps private to this module, perhaps not 
liftST :: ST s a -> Hasher s a 
liftST = Hasher . lift 

----- We can lift the functions which act on an STUArray ----- 

getBounds :: Hasher s (Int,Int) 
getBounds = liftST . A.getBounds =<< ask 

-- I'd recommend against removing the `unsafe` from the name; this function 
-- could segfault, after all. 
unsafeReadBlock :: Int -> Hasher s Word32 
unsafeReadBlock i = do 
    block <- ask 
    liftST $ A.unsafeRead block i 

unsafeWriteBlock :: Int -> Word32 -> Hasher s() 
unsafeWriteBlock i x = do 
    block <- ask 
    liftST $ A.unsafeWrite block i x 

----- And then, perhaps in a separate module: ----- 

padFixed :: Hasher s() 
padFixed = do 
    unsafeWriteBlock 5 0x80000000 
    unsafeWriteBlock 15 160 

(請注意,我不能內嵌hasherToSTrunHasher內,可能是因爲較高等級類型的封鎖推斷。)

基本上,我們將ReaderT (STUArray s Int Word32) (ST s)換成newtype而不是類型同義詞,並將一些基本數組原語提起來,以便在始終可用的塊上工作。如果您不需要,您甚至不需要爲Hasher類型派生MonadReader,只要您解除所有必要的功能即可。但是一旦你完成了這些,你的哈希代碼可以隱式地討論這個數組。

+0

感謝您的徹底解答。我不確定使用'ReaderT' monad是否僅僅使用我當前擁有的簽名(可能會使用'type'替代別名)來購買我,但我需要反思一下你的答案。 – Ralph

-1

可以使用freeze and thaw functions轉換和從UArray

然而,這會導致性能損失,或者您需要使用「不安全」變體。既然你已經在做不安全的寫作,那很可能。

0

不,你很困惑;這是不可能的。將STUArray s i e看作是指向內存塊開始的指針。你必須將該指針傳遞給任何需要修改該塊內存的東西;你不能憑空想象它。

但是你不需要返回它。推測來電者已經有了指針。