हास्केल का उपयोग करने वाले सरणियों की आवश्यकता वाले कार्यों से निपटने का एक अच्छा तरीका क्या है?


11

अक्सर किसी कार्य के लिए वास्तविक सरणियों की आवश्यकता होती है। उदाहरण के लिए Befunge या> <> को लागू करने के लिए कार्य करें। मैंने इसके लिए Arrayमॉड्यूल का उपयोग करने की कोशिश की , लेकिन यह वास्तव में बोझिल है, क्योंकि ऐसा लगता है कि मैं बहुत अधिक क्रिया कोडिंग कर रहा हूं। क्या कोई मेरी मदद कर सकता है कि इस तरह के कोड-गोल्फ कार्यों को कम क्रिया और अधिक कार्यात्मक कैसे हल किया जाए?


AFAIK, यह साइट केवल कोड गोल्फ के लिए है, संबंधित प्रश्नों के लिए नहीं। मुझे लगता है कि यह Stackoverflow पर संबंधित है।
जेसी मिलिकन

@ जेसे मिलिकन: कृपया इस सवाल को भी देखें और पढ़ें। यह बताता है कि, आपको गोल्फ के बारे में कोई प्रश्न पूछने की अनुमति नहीं है। इस तरह के प्रश्न इस साइट की परिभाषा के चरण में "विषय पर" प्रश्न का एक बड़ा हिस्सा थे। कृपया अपने डाउन-डबल को सोचें और इसे हटा दें यदि आप समझते हैं कि मैं यहां यह क्यों पूछ रहा हूं।
फ़ूजक्कल

हम्म, मेरा बुरा मुझे लगता है।
जेसी मिलिकन

@ जेसे मिलिकन: एराएरे ह्युमन इस्ट।
फूज़ेक्नल

सामान्य प्रश्न इसके बारे में बहुत स्पष्ट नहीं है, हालांकि।
जेसी मिलिकन

जवाबों:


5

सबसे पहले, मैं देखने की सलाह देते 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

बैंग पैटर्न संचायक आइटम (कर्सर, संख्या और मानचित्र) के कारण होने वाले स्टैक ओवरफ्लो को बहुत अंत तक उपयोग नहीं करने से रोकते हैं। अधिकांश कोड गोल्फ के लिए, इनपुट मामलों को इस प्रावधान की आवश्यकता के लिए पर्याप्त बड़ा नहीं होना चाहिए।


9

Glib का उत्तर है: सरणियों का उपयोग न करें। नहीं-तो-ग्लिब उत्तर है: अपनी समस्या पर पुनर्विचार करने का प्रयास करें ताकि उसे सरणियों की आवश्यकता न हो।

अक्सर, कुछ विचार के साथ एक समस्या किसी भी सरणी के बिना हो सकती है जैसे कि संरचना। उदाहरण के लिए, यहां यूलर 28 का मेरा जवाब है:

-- | What is the sum of both diagonals in a 1001 by 1001 spiral?
euler28 = spiralDiagonalSum 1001

spiralDiagonalSum n
    | n < 0 || even n = error "spiralDiagonalSum needs a positive, odd number"
    | otherwise = sum $ scanl (+) 1 $ concatMap (replicate 4) [2,4..n]

यहां कोड में जो व्यक्त किया गया है वह संख्याओं के अनुक्रम का पैटर्न है क्योंकि वे आयताकार सर्पिल के चारों ओर बढ़ते हैं। वास्तव में स्वयं संख्याओं के मैट्रिक्स का प्रतिनिधित्व करने की कोई आवश्यकता नहीं थी।

सरणियों से परे सोचने की कुंजी यह सोचना है कि हाथ में समस्या का वास्तव में क्या मतलब है, न कि कैसे आप इसे रैम में बाइट्स के रूप में दर्शा सकते हैं। यह सिर्फ अभ्यास के साथ आता है (शायद एक कारण मैं कोड-गोल्फ इतना!)

एक अन्य उदाहरण यह है कि मैंने अधिकतम मार्ग कोड-गोल्फ को कैसे हल किया । वहां, मैट्रिक्स द्वारा पंक्ति के रूप में एक लहर के रूप में आंशिक समाधानों को पारित करने की विधि, सीधे गुना ऑपरेशन द्वारा व्यक्त की जाती है। याद रखें: अधिकांश सीपीयू पर, आप एक बार में एक पूरे के रूप में सरणी से निपट नहीं सकते हैं: कार्यक्रम समय के साथ इसे संचालित करने जा रहा है। इसे किसी भी समय एक बार में पूरे सरणी की आवश्यकता नहीं हो सकती है।

बेशक, कुछ समस्याओं को इस तरह से कहा गया है कि वे स्वाभाविक रूप से सरणी आधारित हैं। > <>, Befunge, या Brainfuck जैसी भाषाओं में उनके दिल में सरणियाँ होती हैं। हालांकि, वहां भी, सरणियों को अक्सर साथ दिया जा सकता है। उदाहरण के लिए, ब्रेनफैक की व्याख्या करने के लिए मेरा समाधान देखें , इसके शब्दार्थ का वास्तविक मूल एक ज़िप है । इस तरह से सोचना शुरू करने के लिए, पहुंच के पैटर्न और समस्या के अर्थ के करीब संरचना पर ध्यान दें। अक्सर यह एक उत्परिवर्ती सरणी में मजबूर नहीं किया जाना चाहिए।

जब बाकी सब विफल हो जाता है, और आपको एक सरणी का उपयोग करने की आवश्यकता होती है - @ जॉय की युक्तियां एक अच्छी शुरुआत हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.