Frissíthető változók

Frissíthető változók

Frissíthető változó: az imperatív nyelvekből ismert változó.

OCaml (* C *):

ref  : 'a          -> 'a ref   (* &a     *)
(!)  : 'a ref      -> 'a       (* *p     *)
(:=) : 'a ref -> a ->  unit    (* *p = a *)

Haskell:

 -- Data.IORef modul
-- newIORef   ::       a      -> IO (IORef a)
-- readIORef  :: IORef a      -> IO a
-- writeIORef :: IORef a -> a -> IO ()

A frissíthető változók típusa IORef a, ahol a a hivatkozott érték típusa.

Példa

fib :: Int -> IO Integer
fib n = do
   x <- newIORef 0    -- új változó
   y <- newIORef 1
   forM_ [1..n] $ \i -> do -- ciklus
       a <- readIORef x       -- olvasás
       b <- readIORef y
       writeIORef x b         -- írás
       writeIORef y (a + b)
   readIORef x

ahol

forM_ :: Monad m => [a] -> (a -> m b) -> m ()
forM_ = flip mapM_

Feladat

Definiáljuk újra a modifyIORef függvényt!

-- modifyIORef :: IORef a -> (a -> a) -> IO ()
modifyIORef ref f = readIORef ref >>= writeIORef ref . f

Feladat

Definiáljuk a swap függvényt, ami két frissíthető változó tartalmát felcseréli!

-- swap :: IORef a → IORef a → IO ()
swap x y = do
    a  readIORef x
    b  readIORef y
    writeIORef x b
    writeIORef y a

Feladat

Definiáljunk egy függvényt ami egy frissíthető változó tartalmát kiírja a képernyőre!

-- printIORef :: Show a => IORef a -> IO ()
printIORef x = do
  a <- readIORef x
  print a

Feladat

Definiáljuk a sumOfSquares függvényt, ami visszaad egy akciót ami egy x frissíthető változóba gyűjtve kiszámolja 1-től n-ig a számok négyzetösszegét, és a részeredményeket kiírja a képernyőre!

sumOfSquares :: Integer -> IO Integer
sumOfSquares n = do
  x <- newIORef 0
  forM_ [1..n] $ \i -> do
      modifyIORef x (+ i^2)
      printIORef x
  readIORef x

Hivatkozási helyfüggetlenség

example= do
    x  newIORef (13 :: Int)
    y  newIORef 14
    a  readIORef x
    print a          
    swap x y
    a  readIORef x
    print a          

A hivatkozási helyfüggetlenség megmarad mert readIORef x mindkétszer ugyanazt a számítást jelöli (az x változó olvasását), print a pedig azért más mert a két a változó különböző.

Címkézett frissíthető változók

fib típusa Int -> IO Integer így csak akciók számolhatnak Fibonacci számokat.

Megoldás: címkézett frissíthető változók

Alapgondolat:
Ha a számítás csak “magán” változókat használ, akkor végeredménye kiértékelhető.

Alapműveletek

Megcímkézzük az IORef típust: IORef a –> STRef s a.

Megcímkézzük az IO típust: IO a –> ST s a.

x :: ST s a egy számítás ami csak az s-sel címkézett változókhoz férhet hozzá.

newSTRef :: a -> ST s (STRef s a)
readSTRef :: STRef s a -> ST s a
writeSTRef :: STRef s a -> a -> ST s ()

Példa

A kód átnevezéseken kívül itt nem változik:

fib' :: Int -> ST s Integer
fib' n = do
    x <- newSTRef 0
    y <- newSTRef 1
    forM_ [1..n] $ \i -> do
        a <- readSTRef x
        b <- readSTRef y
        writeSTRef x b
        writeSTRef y (a + b)
    readSTRef x

Példa (folytatás)

 -- {-# LANGUAGE RankNTypes #-}
runST :: (forall s. ST s a) -> a

Az utolsó lépés:

pureFib :: Int -> Integer

Test>
True :: Bool
Test>
True :: Bool

A címkézett frissíthető változók típusa STRef s a, ahol s a címke, a pedig a hivatkozott érték típusa.

Mivel a típuslevezetés során az közbeeső akciókon is fel kell tüntetni a címkét, ezért ezeknek az akcióknak a típusa nem a szokásos IO a, hanem ST s a, ahol s a címke, a pedig a visszatérési érték típusa.

A runST függvény értelmezése a következő. Mivel az s változót a belső forall vezeti be, ezért a nem specializálható olyan típusra, amelyben szerepel s. Ebből következően az ST s a típusú számítás végeredménye független s-től.

Feladat

Írjuk újra a korábbi feladatban szereplő sumOfSquares függvényt frissíthető változók segítségével! (A részeredményeket kiíró sort szedjük ki.)

sumOfSquares' :: Integer -> Integer

Test>
True :: Bool

Feladat

Vegyünk egy rövid imperatív algoritmust és próbáljuk meg átírni szó szerint Haskellre frissíthető változókkal. Ezután próbálkozhatunk címkézett frissíthető változókkal is.

Javaslat: Egy-két művelet egyszeresen láncolt listákon