3

を受け入れ、STUArrayいくつかの要素を変更し、変更された配列を返す小さな Haskell 関数があります。ST s (STUArray s Int Word32)モナドで動作する別の関数から呼び出されます。PBKDF2私が書こうとしている高速関数の一部です。この関数は、固定サイズのメッセージ (160 ビット) に対して SHA-1 パディングを行います。

これが私のコードです:

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に従って、必要なパディングが追加されます。

モナドから配列を「取り出し」、それに取り組み、それを元に戻すように書き直すにはどうすればよいですか?署名はpadFixed :: ST s (STUArray s Int Word32)blockパラメータなしである必要があります。

これは可能ですか?モナドから配列を抽出できる関数がライブラリに見つかりませんでしたが、何かを見逃していたのかもしれません。

に関する適切なチュートリアルはありSTArrayますか?

4

3 に答える 3

4

いいえ、できません。STこれらのセマンティクスはありません。モナドはST sであり、 ではありませんST s (STUArray s a)ST s変更可能な状態を追跡するための単なるモナドです。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 ->です。type の値Reader r aは単なる関数r -> aです。aこれは基本的に、 type の値にアクセスしながら計算する方法rです。モナド変換子はReaderT r、型の変数へのアクセスをr任意のモナド計算に提供することを可能にします。したがって、ReaderT (STUArray s Int Word32) (ST s)あるST s配列にアクセスできる計算です。から配列を返す必要がないことに注意してくださいpadFixed。モナドバインドはそのすべてを処理します。

ask配列に対して ingを保持する必要があるため、これを記述するのは少し面倒です。幸いなことに、これを処理するコンビネータをいくつか書くことができます。

{-# 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

(おそらく、上位の型が推論をブロックしているため、 のhasherToST内部にインライン化できなかったことに注意してください。)runHasher

基本的に、型シノニムの代わりに をラップし、ReaderT (STUArray s Int Word32) (ST s)いくつnewtypeかの基本的な配列プリミティブを持ち上げて、常に利用可能なブロックで動作するようにします。必要なすべての関数を持ち上げる限り、必要がなければ型を派生させる必要さえありませMonadReaderん。Hasherしかし、一度これを行うと、ハッシュ コードは暗黙的に配列について話すことができます。

于 2013-10-21T21:03:59.613 に答える
0

いいえ、あなたは混乱しています。それは可能ではありません。STUArray s i eメモリのブロックの先頭へのポインタであると考えてください。そのメモリ ブロックを変更する必要があるものには、そのポインターを渡す必要があります。何もないところから思い出すことはできません。

しかし、あなたはそれを返す必要はありません。おそらく、呼び出し元は既にポインターを持っています。

于 2013-10-21T20:46:03.800 に答える
-1

freeze関数とthaw関数を使用して、 との間で変換できますUArray

ただし、これによりパフォーマンスが低下するか、「安全でない」バリアントを使用する必要があります。すでに安全でない書き込みを行っているので、おそらく問題ありません。

于 2013-10-21T20:45:50.687 に答える