कार्यात्मक प्रोग्रामिंग से छुटकारा नहीं मिलता है। यह केवल यह स्पष्ट करता है! हालांकि यह सच है कि मानचित्र जैसे कार्य अक्सर "साझा" किए गए डेटा संरचना को "अनवील" करते हैं, यदि आप जो करना चाहते हैं वह एक रीचबिलिटी एल्गोरिदम लिख रहा है तो यह केवल उन नोड्स का ट्रैक रखने की बात है जो आपने पहले से ही देखे हैं।
import qualified Data.Set as S
data Node = Node Int [Node] deriving (Show)
-- Receives a root node, returns a list of the node keyss visited in a depth-first search
dfs :: Node -> [Int]
dfs x = fst (dfs' (x, S.empty))
-- This worker function keeps track of a set of already-visited nodes to ignore.
dfs' :: (Node, S.Set Int) -> ([Int], S.Set Int)
dfs' (node@(Node k ns), s )
| k `S.member` s = ([], s)
| otherwise =
let (childtrees, s') = loopChildren ns (S.insert k s) in
(k:(concat childtrees), s')
--This function could probably be implemented as just a fold but Im lazy today...
loopChildren :: [Node] -> S.Set Int -> ([[Int]], S.Set Int)
loopChildren [] s = ([], s)
loopChildren (n:ns) s =
let (xs, s') = dfs' (n, s) in
let (xss, s'') = loopChildren ns s' in
(xs:xss, s'')
na = Node 1 [nb, nc, nd]
nb = Node 2 [ne]
nc = Node 3 [ne, nf]
nd = Node 4 [nf]
ne = Node 5 [ng]
nf = Node 6 []
ng = Node 7 []
main = print $ dfs na -- [1,2,5,7,3,6,4]
अब, मुझे यह स्वीकार करना चाहिए कि इस सारे राज्य पर नज़र रखना बहुत कष्टप्रद और त्रुटि प्रवण है (s 'के बजाय s' का उपयोग करना आसान है, एक ही s 'को एक से अधिक संगणना के लिए पास करना आसान है ...) । यह वह जगह है जहां सन्यासी आते हैं: वे कुछ भी नहीं जोड़ते हैं जो आप पहले से ही नहीं कर सकते थे, लेकिन वे आपको अनुमानित रूप से राज्य चर पास करते हैं और इंटरफ़ेस गारंटी देता है कि यह एकल-थ्रेडेड तरीके से होता है।
संपादित करें: मैं अब जो कुछ भी किया था, उसके बारे में अधिक तर्क देने का प्रयास करूंगा: सबसे पहले, केवल रीचैबिलिटी के परीक्षण के बजाय मैंने एक गहराई-पहली खोज को कोडित किया। कार्यान्वयन बहुत ज्यादा दिखने वाला है लेकिन डिबगिंग थोड़ा बेहतर है।
एक राज्य भाषा में, DFS इस तरह दिखाई देगा:
visited = set() #mutable state
visitlist = [] #mutable state
def dfs(node):
if isMember(node, visited):
//do nothing
else:
visited[node.key] = true
visitlist.append(node.key)
for child in node.children:
dfs(child)
अब हमें उत्परिवर्तित स्थिति से छुटकारा पाने का एक रास्ता खोजने की जरूरत है। सबसे पहले हम "विज़िटसूची" चर से छुटकारा पाने के लिए dfs रिटर्न बनाते हैं जो शून्य के बजाय:
visited = set() #mutable state
def dfs(node):
if isMember(node, visited):
return []
else:
visited[node.key] = true
return [node.key] + concat(map(dfs, node.children))
और अब मुश्किल हिस्सा आता है: "विज़िट किए गए" चर से छुटकारा। मूल चाल एक सम्मेलन का उपयोग करना है जहां हम राज्य को उन कार्यों के लिए एक अतिरिक्त पैरामीटर के रूप में पास करते हैं जिनकी आवश्यकता होती है और उन कार्यों को राज्य के नए संस्करण को अतिरिक्त रिटर्न मान के रूप में वापस करना होता है यदि वे इसे संशोधित करना चाहते हैं।
let increment_state s = s+1 in
let extract_state s = (s, 0) in
let s0 = 0 in
let s1 = increment_state s0 in
let s2 = increment_state s1 in
let (x, s3) = extract_state s2 in
-- and so on...
इस पैटर्न को dfs में लागू करने के लिए, हमें अतिरिक्त पैरामीटर के रूप में "विज़िट किए गए" सेट को प्राप्त करने के लिए और अतिरिक्त रिटर्न मान के रूप में "विज़िट" के अपडेट किए गए संस्करण को वापस करने के लिए इसे बदलना होगा। इसके अतिरिक्त, हमें कोड को फिर से लिखना होगा ताकि हम हमेशा "विज़िट किए गए" सरणी के "सबसे हाल के" संस्करण को आगे बढ़ाएं:
def dfs(node, visited1):
if isMember(node, visited1):
return ([], visited1) #return the old state because we dont want to change it
else:
curr_visited = insert(node.key, visited1) #immutable update, with a new variable for the new value
childtrees = []
for child in node.children:
(ct, curr_visited) = dfs(child, curr_visited)
child_trees.append(ct)
return ([node.key] + concat(childTrees), curr_visited)
हास्केल संस्करण बहुत कुछ करता है जो मैंने यहां किया था, सिवाय इसके कि यह सभी तरह से चला जाता है और उत्परिवर्तित "कर्वस" और "चाइल्डट्रीज़" चर के बजाय एक आंतरिक पुनरावर्ती फ़ंक्शन का उपयोग करता है।
भिक्षुओं के लिए, जो वे मूल रूप से पूरा करते हैं, वह "हाथ में लेने के लिए मजबूर करने के बजाय," वक्र_विचारित "होता है। यह न केवल कोड से अव्यवस्था को हटाता है, बल्कि यह आपको गलतियों को करने से भी रोकता है, जैसे कि फोर्किंग स्टेट (राज्य का पीछा करने के बजाय दो बाद की कॉल पर सेट "समान" का दौरा किया)।