सबसे पहले, मैं देखने की सलाह देते Data.Vector , करने के लिए एक अच्छा विकल्प Data.Array कुछ मामलों में।
Array
और Vector
कुछ संस्मरण मामलों के लिए आदर्श हैं, जैसा कि "अधिकतम पथ ढूँढना" के मेरे उत्तर में प्रदर्शित किया गया है । हालाँकि, कुछ समस्याओं को केवल एक कार्यात्मक शैली में व्यक्त करना आसान नहीं है। उदाहरण के लिए, प्रोजेक्ट ईयूलर में समस्या 28 एक सर्पिल के विकर्णों पर संख्याओं को समेटने के लिए कहता है। निश्चित रूप से, इन नंबरों के लिए एक फार्मूला खोजना बहुत आसान होना चाहिए, लेकिन सर्पिल का निर्माण करना अधिक चुनौतीपूर्ण है।
Data.Array.ST एक परिवर्तनशील सरणी प्रकार प्रदान करता है। हालांकि, प्रकार स्थिति गड़बड़ है: यह एक का उपयोग करता वर्ग MArray के लिए छोड़कर अपने तरीकों में से हर एक ओवरलोड runSTArray । इसलिए, जब तक आप एक उत्परिवर्ती सरणी कार्रवाई से एक अपरिवर्तनीय सरणी वापस करने की योजना नहीं बनाते हैं, आपको एक या अधिक प्रकार के हस्ताक्षर जोड़ने होंगे:
import Control.Monad.ST
import Data.Array.ST
foo :: Int -> [Int]
foo n = runST $ do
a <- newArray (1,n) 123 :: ST s (STArray s Int Int) -- this type signature is required
sequence [readArray a i | i <- [1..n]]
main = print $ foo 5
फिर भी, यूलर 28 के लिए मेरा समाधान बहुत अच्छी तरह से निकला, और मुझे उस प्रकार के हस्ताक्षर की आवश्यकता नहीं थी क्योंकि मैंने उपयोग किया था runSTArray
।
Data.ap का उपयोग "म्यूटेबल ऐरे" के रूप में करें
यदि आप एक परिवर्तनशील सरणी एल्गोरिथ्म को लागू करना चाहते हैं, तो Data.Map का उपयोग करने का एक और विकल्प है । जब आप एक सरणी का उपयोग करते हैं, तो आप चाहते हैं कि आपके पास एक फ़ंक्शन ऐसा हो, जो किसी सरणी के एकल तत्व को बदल दे:
writeArray :: Ix i => i -> e -> Array i e -> Array i e
दुर्भाग्य से, इसे पूरे सरणी की प्रतिलिपि बनाने की आवश्यकता होगी, जब तक कि कार्यान्वयन संभव से बचने के लिए कॉपी-ऑन-राइट रणनीति का उपयोग नहीं करता।
अच्छी खबर यह है, Data.Map
इस तरह एक समारोह है, सम्मिलित करें :
insert :: Ord k => k -> a -> Map k a -> Map k a
क्योंकि Map
आंतरिक रूप से एक संतुलित बाइनरी ट्री के रूप में कार्यान्वित किया जाता है, insert
केवल O (लॉग एन) समय और स्थान लेता है, और मूल प्रति को संरक्षित करता है। इसलिए, Map
न केवल कुछ हद तक कुशल "म्यूटेबल एरे" प्रदान करता है जो कार्यात्मक प्रोग्रामिंग मॉडल के साथ संगत है, लेकिन यह आपको "समय पर वापस जाने" की सुविधा भी देता है यदि आप ऐसा करते हैं।
यहाँ Data.Map का उपयोग कर यूलर 28 का एक समाधान है:
{-# LANGUAGE BangPatterns #-}
import Data.Map hiding (map)
import Data.List (intercalate, foldl')
data Spiral = Spiral Int (Map (Int,Int) Int)
build :: Int -> [(Int,Int)] -> Map (Int,Int) Int
build size = snd . foldl' move ((start,start,1), empty) where
start = (size-1) `div` 2
move ((!x,!y,!n), !m) (dx,dy) = ((x+dx,y+dy,n+1), insert (x,y) n m)
spiral :: Int -> Spiral
spiral size
| size < 1 = error "spiral: size < 1"
| otherwise = Spiral size (build size moves) where
right = (1,0)
down = (0,1)
left = (-1,0)
up = (0,-1)
over n = replicate n up ++ replicate (n+1) right
under n = replicate n down ++ replicate (n+1) left
moves = concat $ take size $ zipWith ($) (cycle [over, under]) [0..]
spiralSize :: Spiral -> Int
spiralSize (Spiral s m) = s
printSpiral :: Spiral -> IO ()
printSpiral (Spiral s m) = do
let items = [[m ! (i,j) | j <- [0..s-1]] | i <- [0..s-1]]
mapM_ (putStrLn . intercalate "\t" . map show) items
sumDiagonals :: Spiral -> Int
sumDiagonals (Spiral s m) =
let total = sum [m ! (i,i) + m ! (s-i-1, i) | i <- [0..s-1]]
in total-1 -- subtract 1 to undo counting the middle twice
main = print $ sumDiagonals $ spiral 1001
बैंग पैटर्न संचायक आइटम (कर्सर, संख्या और मानचित्र) के कारण होने वाले स्टैक ओवरफ्लो को बहुत अंत तक उपयोग नहीं करने से रोकते हैं। अधिकांश कोड गोल्फ के लिए, इनपुट मामलों को इस प्रावधान की आवश्यकता के लिए पर्याप्त बड़ा नहीं होना चाहिए।