शुद्ध हैकेल में यह संभव नहीं है, प्रश्न Can a thunk be duplicated to improve memory performance? देखें (जैसा कि @shang द्वारा इंगित किया गया है)। हालांकि, आप इसे आईओ के साथ कर सकते हैं।
हम मॉड्यूल हेड के साथ शुरू करते हैं और केवल उस प्रकार और फ़ंक्शंस को सूचीबद्ध करते हैं जो इस मॉड्यूल को बनाना चाहिए (जो unsafePerformIO का उपयोग करेगा) सुरक्षित। बिना असुरक्षित पैराफॉर्मियो के ऐसा करना भी संभव है, लेकिन इसका मतलब यह होगा कि उपयोगकर्ता को आईओ में अपना अधिक कोड रखना होगा।
{-# LANGUAGE ExistentialQuantification #-}
module ReEval (ReEval, newReEval, readReEval, resetReEval) where
import Data.IORef
import System.IO.Unsafe
हम जानते हैं कि एक तरीका है कि सभी साझा करने से बचाता है, समारोह और एक दूसरे से दूर तर्क रख कर में कोई मान संग्रहीत करता है, और केवल समारोह लागू होते हैं जब हम मूल्य चाहते हैं एक डेटा प्रकार परिभाषित करते हुए शुरू करते हैं। नोट unsharedValue
द्वारा दिए गए मान साझा किया जा सकता है, लेकिन अन्य आमंत्रण की वापसी मान के साथ नहीं (समारोह संभालने कुछ गैर तुच्छ कर रही है):
data Unshared a = forall b. Unshared (b -> a) b
unsharedValue :: Unshared a -> a
unsharedValue (Unshared f x) = f x
अब हम द्वारा रीसेट करने संगणना के बारे में हमारी डेटा के प्रकार को परिभाषित । हमें गणना और वर्तमान मूल्य को स्टोर करने की आवश्यकता है। उत्तरार्द्ध IORef
में संग्रहीत है, क्योंकि हम इसे रीसेट करने में सक्षम होना चाहते हैं।
data ReEval a = ReEval {
calculation :: Unshared a,
currentValue :: IORef a
}
एक ReEval
बॉक्स में कोई मान लपेट के लिए, हम एक समारोह और एक बहस की आवश्यकता है। क्यों न केवल a -> ReEval a
? क्योंकि पैरामीटर को साझा करने से रोकने का कोई तरीका नहीं होगा।
newReEval :: (b -> a) -> b -> ReEval a
newReEval f x = unsafePerformIO $ do
let c = Unshared f x
ref <- newIORef (unsharedValue c)
return $ ReEval c ref
पढ़ना आसान है: बस IORef
से मूल्य प्राप्त करें। unsafePerformIO
का यह उपयोग सुरक्षित है क्योंकि हम हमेशा unsharedValue c
का मान प्राप्त करेंगे, हालांकि इसकी एक अलग "प्रति" है।
readReEval :: ReEval a -> a
readReEval r = unsafePerformIO $ readIORef (currentValue r)
और अंततः रीसेटिंग। मैंने इसे आईओ मोनैड में छोड़ा, क्योंकि यह unsafePerformIO
में लपेटने वाले अन्य फ़ंक्शन से कम सुरक्षित नहीं होगा, लेकिन पर उपयोगकर्ता नियंत्रण देने का यह सबसे आसान तरीका है जब रीसेटिंग वास्तव में होती है।आप जोखिम नहीं लेना चाहते हैं कि resetReEval
पर आपकी सभी कॉल आलसी देरी हो रही हैं जब तक कि आपकी मेमोरी समाप्त न हो या यहां तक कि अनुकूलित भी हो क्योंकि उपयोग करने के लिए कोई वापसी मूल्य नहीं है।
resetReEval :: ReEval a -> IO()
resetReEval r = writeIORef (currentValue r) (unsharedValue (calculation r))
यह मॉड्यूल का अंत है। यहाँ उदाहरण कोड है:
import Debug.Trace
import ReEval
main = do
let func a = trace ("func " ++ show a) negate a
let l = [ newReEval func n | n <- [1..5] ]
print (map readReEval l)
print (map readReEval l)
mapM_ resetReEval l
print (map readReEval l)
और यहाँ आप यह क्या उम्मीद करता है कि देख सकते हैं:
$ runhaskell test.hs
func 1
func 2
func 3
func 4
func 5
[-1,-2,-3,-4,-5]
[-1,-2,-3,-4,-5]
func 1
func 2
func 3
func 4
func 5
[-1,-2,-3,-4,-5]
संबंधित: http://stackoverflow.com/questions/11675807/can-a-thunk-be - डुप्लिकेट-टू-सुधार-मेमोरी-प्रदर्शन – shang
साझा करने के लिए यह एक दिलचस्प चाल @shang धन्यवाद है। – Davorak
@ipsec मुझे आश्चर्य होगा अगर कोई जवाब है जो आपको शुद्ध मोनैड या आईओ मोनड में नहीं डालता है। इंटरफ़ेस शुद्ध होना चाहिए क्योंकि आप असुरक्षित प्रीफॉर्मियो से दूर हो सकते हैं। क्या उन पंक्तियों के साथ कुछ आपके लिए काम करेगा? – Davorak