Haskell でStackを(3)

今度はスタックのサイズは固定、要素もIntのみで、状態モナドを止めてみた。スタック自身でポインタを管理する。これで複数のスタックを使える。ほぼCの構造体になったと思うがわたしはCを書けないので自信なし。

import Foreign.Ptr
import Foreign.Marshal.Alloc
import Foreign.Marshal.Array
import Foreign.Storable

stackSize :: Int
stackSize = 20

-- スタックのインデックスとスタック本体へのポインタ
data Stack = Stack Int (Ptr Int) deriving Show

instance Storable Stack where
  alignment _ = 16
  sizeOf _ = 16
  peek ptr = do
    n <- peek (castPtr ptr)
    p <- peek (castPtr ptr `plusPtr` 8)
    return $ Stack n p
  poke ptr (Stack n p) = do
    poke (castPtr ptr) n
    poke (castPtr ptr `plusPtr` 8) p

mkStack :: IO (Ptr Stack)
mkStack = do
  p <- malloc :: IO (Ptr Stack)
  s <- mallocArray stackSize
  poke p (Stack 0 s)
  return p

push :: Ptr Stack -> Int -> IO (Ptr Stack)
push st x = do
  Stack i p <- peek st
  if i >= stackSize then
    error "oops. Stack overflow"
    else
      do
        pokeElemOff p i x
        poke st (Stack (i + 1) p) 
        return st

pop :: Ptr Stack -> IO Int
pop st = do                       
  Stack i p <- peek st
  if i <= 0 then error "oops. Stack underflow"
    else
      do
        x <- peekElemOff p (i - 1)
        poke st (Stack (i - 1) p)
        return x

calc :: (Int -> Int -> Int) -> Ptr Stack -> IO (Ptr Stack)
calc f st = do
  x <- pop st
  y <- pop st
  push st (f x y)

add :: Ptr Stack -> IO (Ptr Stack)
add = calc (+)
sub :: Ptr Stack -> IO (Ptr Stack)
sub = calc (-)

使い方はこのように変わる。

test0 = do
  st <- mkStack
  push st 9
  pop st