मैं एक समाधान के साथ आया जो हास्केल प्रकार प्रणाली का उपयोग करता है। मैंने मूल्य स्तर पर समस्या के मौजूदा समाधान के लिए थोड़ा सा googled , इसे थोड़ा बदल दिया, और फिर इसे प्रकार स्तर तक उठा लिया। इस पर लगाम लगी। मुझे GHC एक्सटेंशन का एक गुच्छा भी सक्षम करना पड़ा।
सबसे पहले, चूंकि पूर्णांक को टाइप स्तर पर अनुमति नहीं है, इसलिए मुझे इस समय प्राकृतिक संख्याओं को एक बार फिर से बढ़ाने की आवश्यकता है, इस प्रकार:
data Zero -- type that represents zero
data S n -- type constructor that constructs the successor of another natural number
-- Some numbers shortcuts
type One = S Zero
type Two = S One
type Three = S Two
type Four = S Three
type Five = S Four
type Six = S Five
type Seven = S Six
type Eight = S Seven
मैंने जिस एल्गोरिथम को अनुकूलित किया है, वह भीलों पर परिवर्धन और घटाव करता है, इसलिए मुझे इन पर भी लगाम कसनी थी। प्रकार के स्तर पर कार्य को प्रकारों के लिए रिज़ॉर्ट के साथ परिभाषित किया जाता है। इसके लिए कई पैरामीटर प्रकार की कक्षाओं और कार्यात्मक निर्भरताओं के लिए एक्सटेंशन की आवश्यकता होती है। टाइप कक्षाएं "मान वापस नहीं" कर सकती हैं, इसलिए हम PROLOG के समान एक अतिरिक्त पैरामीटर का उपयोग करते हैं।
class Add a b r | a b -> r -- last param is the result
instance Add Zero b b -- 0 + b = b
instance (Add a b r) => Add (S a) b (S r) -- S(a) + b = S(a + b)
class Sub a b r | a b -> r
instance Sub a Zero a -- a - 0 = a
instance (Sub a b r) => Sub (S a) (S b) r -- S(a) - S(b) = a - b
पुनर्संयोजन को वर्ग के सिद्धांतों के साथ लागू किया जाता है, इसलिए वाक्यविन्यास थोड़ा पीछे दिखता है।
अगले बूलियन थे:
data True -- type that represents truth
data False -- type that represents falsehood
और असमानता तुलना करने के लिए एक समारोह:
class NotEq a b r | a b -> r
instance NotEq Zero Zero False -- 0 /= 0 = False
instance NotEq (S a) Zero True -- S(a) /= 0 = True
instance NotEq Zero (S a) True -- 0 /= S(a) = True
instance (NotEq a b r) => NotEq (S a) (S b) r -- S(a) /= S(b) = a /= b
और सूचियाँ ...
data Nil
data h ::: t
infixr 0 :::
class Append xs ys r | xs ys -> r
instance Append Nil ys ys -- [] ++ _ = []
instance (Append xs ys rec) => Append (x ::: xs) ys (x ::: rec) -- (x:xs) ++ ys = x:(xs ++ ys)
class Concat xs r | xs -> r
instance Concat Nil Nil -- concat [] = []
instance (Concat xs rec, Append x rec r) => Concat (x ::: xs) r -- concat (x:xs) = x ++ concat xs
class And l r | l -> r
instance And Nil True -- and [] = True
instance And (False ::: t) False -- and (False:_) = False
instance (And t r) => And (True ::: t) r -- and (True:t) = and t
if
s भी प्रकार के स्तर पर गायब हैं ...
class Cond c t e r | c t e -> r
instance Cond True t e t -- cond True t _ = t
instance Cond False t e e -- cond False _ e = e
और इसके साथ, मेरे द्वारा उपयोग की जाने वाली सभी सहायक मशीनरी की जगह थी। समय ही समस्या से निपटने के लिए!
एक मौजूदा बोर्ड में एक रानी को जोड़ने के लिए परीक्षण करने के लिए एक समारोह के साथ शुरू करना ठीक है:
-- Testing if it's safe to add a queen
class Safe x b n r | x b n -> r
instance Safe x Nil n True -- safe x [] n = True
instance (Safe x y (S n) rec,
Add c n cpn, Sub c n cmn,
NotEq x c c1, NotEq x cpn c2, NotEq x cmn c3,
And (c1 ::: c2 ::: c3 ::: rec ::: Nil) r) => Safe x (c ::: y) n r
-- safe x (c:y) n = and [ x /= c , x /= c + n , x /= c - n , safe x y (n+1)]
मध्यवर्ती परिणामों को प्राप्त करने के लिए वर्ग अभिकथनों के उपयोग पर ध्यान दें। क्योंकि रिटर्न मान वास्तव में एक अतिरिक्त पैरामीटर हैं, हम सिर्फ एक दूसरे से सीधे दावे को नहीं कह सकते। फिर, यदि आपने इस शैली को थोड़ा परिचित पाया जा सकता है, तो इससे पहले आपने PROLOG का उपयोग किया है।
मेमने की आवश्यकता को दूर करने के लिए कुछ बदलाव करने के बाद (जिसे मैं लागू कर सकता था, लेकिन मैंने एक और दिन छोड़ने का फैसला किया), यह वही है जो मूल समाधान की तरह दिखता है:
queens 0 = [[]]
-- The original used the list monad. I "unrolled" bind into concat & map.
queens n = concat $ map f $ queens (n-1)
g y x = if safe x y 1 then [x:y] else []
f y = concat $ map (g y) [1..8]
map
एक उच्च क्रम फ़ंक्शन है। मैंने सोचा था कि उच्च-आदेश मेटा-फ़ंक्शंस लागू करने में बहुत अधिक परेशानी होगी (फिर से लैम्ब्डा) इसलिए मैं बस एक सरल समाधान के साथ गया: क्योंकि मुझे पता है कि क्या कार्यों को मैप किया जाएगा, मैं map
प्रत्येक के लिए विशेष संस्करण लागू कर सकता हूं , ताकि वे न हों उच्च क्रम के कार्य।
-- Auxiliary meta-functions
class G y x r | y x -> r
instance (Safe x y One s, Cond s ((x ::: y) ::: Nil) Nil r) => G y x r
class MapG y l r | y l -> r
instance MapG y Nil Nil
instance (MapG y xs rec, G y x g) => MapG y (x ::: xs) (g ::: rec)
-- Shortcut for [1..8]
type OneToEight = One ::: Two ::: Three ::: Four ::: Five ::: Six ::: Seven ::: Eight ::: Nil
class F y r | y -> r
instance (MapG y OneToEight m, Concat m r) => F y r -- f y = concat $ map (g y) [1..8]
class MapF l r | l -> r
instance MapF Nil Nil
instance (MapF xs rec, F x f) => MapF (x ::: xs) (f ::: rec)
और अंतिम मेटा-फंक्शन अब लिखा जा सकता है:
class Queens n r | n -> r
instance Queens Zero (Nil ::: Nil)
instance (Queens n rec, MapF rec m, Concat m r) => Queens (S n) r
जो कुछ बचा है, वह किसी प्रकार के ड्राइवर को समाधानों को हल करने के लिए टाइप-चेकिंग मशीनरी को समेटने के लिए है।
-- dummy value of type Eight
eight = undefined :: Eight
-- dummy function that asserts the Queens class
queens :: Queens n r => n -> r
queens = const undefined
यह मेटा-प्रोग्राम टाइप चेकर पर चलने वाला है, इसलिए कोई व्यक्ति आग लगा सकता है ghci
और टाइप के लिए पूछ सकता है queens eight
:
> :t queens eight
यह तेजी से डिफ़ॉल्ट पुनरावृत्ति सीमा को पार कर जाएगा (यह एक औसत 20 है)। इस सीमा को बढ़ाने के लिए, हमें विकल्प के ghci
साथ आमंत्रित करने की आवश्यकता है -fcontext-stack=N
, जहां N
वांछित स्टैक डेप्थ (एन = 1000 और पंद्रह मिनट पर्याप्त नहीं है)। मैंने इसे अभी तक पूरा होने के लिए नहीं देखा है, क्योंकि इसमें बहुत लंबा समय लगता है, लेकिन मैं इसे चलाने में कामयाब रहा queens four
।
नहीं है एक पूरा कार्यक्रम बहुत परिणाम प्रकार मुद्रण के लिए कुछ मशीनरी के साथ ideone पर है, लेकिन वहाँ केवल queens two
सीमा से अधिक के बिना चला सकते :(