2011-02-06 16 views
5

Mam procedurę, która (a) wykonuje niektóre operacje wejścia/wyjścia, (b) tworzy tabelę odnośników, i (c) zwraca operację we/wy, która używa tabeli odnośników. Jednak po skompilowaniu z -O, GHC (wersja 6.12.1) naświetla konstrukcję tabeli odnośników, dzięki czemu jest ona ponownie oceniana dla każdego wywołania działania IO.Powtórna ocena czystego wyrażenia w akcji IO

Przykład:

module Main where 
import Data.Array 
import Data.IORef 
import Control.Monad 

makeAction getX getY sumRef = do 
    x <- getX 
    let a = listArray (0, 1000) [x ..] 
    return $ do 
     y <- getY 
     modifyIORef sumRef (\sum -> sum + a ! y) 

main = do 
    sumRef <- newIORef 0 
    action <- makeAction getX getY sumRef 
    replicateM_ 100000 action 
    n <- readIORef sumRef 
    putStrLn (show n) 
    where 
    getX = return (1 :: Int) 
    getY = return 0 

Jest to problem znany wystarczy mieć standardową GHC-niezawodny obejście - albo jak można ustawić program tak, że a nie jest wielokrotnie są rozdzielone?

+0

Czy próbowałeś pragma '{- # NOINLINE # -}'? – fuz

+0

@FUZxxl Tak, '{- # NOINLINE a # -}' na lokalnym 'a' nie robi tego. – antonakos

+0

Jeśli chcesz tabeli odnośników i chcesz podzielić się nią z powtarzającymi się czynnościami, z pewnością należy oddzielić tworzenie go od funkcji @ makeAction @? Wygląda na to, że makeAction będzie tworzyć tablicę za każdym razem, niezależnie od inliningu. Może powinieneś używać IOArray wyciągniętej z funkcji makeAction. –

Odpowiedz

4

Najprostszym rozwiązaniem jest zmusić ocenę za pomocą adnotacji surowości.

{-# LANGUAGE BangPatterns #-} 

Następnie wymusić podział prostu co a ścisłego użyciu ! ("Bang").

let !a = listArray (0, 1000) [x ..] 

Alternatywnie, jeśli pracujesz w IO monady, surowość adnotacje nie zawsze może pomóc. Aby wymusić ocenę wyrażenia przed uruchomieniem jakiejś akcji, można użyć evaluate. Na przykład:

let a = listArray (0, 1000) [x ..] 
    evaluate a 
2

spróbuj wymusić a przy konstruowaniu wartości jednowartościowy powrotu:

makeAction getX getY sumRef = do 
    x <- getX 
    let a = listArray (0, 1000) [x ..] 
    return $ a `seq` do 
     y <- getY 
     modifyIORef sumRef (\sum -> sum + a ! y)