Elm जैसी प्योर फंक्शनल भाषा में, एक ही आर्गुमेंट के साथ कॉल किया गया फंक्शन हमेशा वही वैल्यू लौटाता है।
इसलिए rand : Int टाइप सिग्नेचर वाले फंक्शन को केवल rand = 4 के रूप में ही बनाया जा सकता है, जो रैंडम पूर्णांक बनाने की दिशा में ठीक नहीं है।
तो Elm में रैंडम वैल्यू हम कैसे बनाते हैं?
हम इस समस्या को दो हिस्सों में बाँटते हैं: पहले हम Random.Generator a की मदद से यह बताते हैं कि हम कौन-सी वैल्यू बनाना चाहते हैं, और फिर वह वैल्यू बनाते हैं।
वैल्यू बनाने का पहला तरीका यह है कि initialSeed : Int -> Seed की मदद से एक Random.Seed बनाया जाए और फिर step : Generator a -> Seed -> ( a, Seed ) का उपयोग किया जाए, जो एक वैल्यू और एक नया सीड लौटाता है।
ध्यान दीजिए कि ये दोनों फंक्शन प्योर हैं। इसलिए इन्हें एक ही आर्गुमेंट के साथ दो बार कॉल करने पर हमेशा वही वैल्यू मिलेंगी।
वैल्यू बनाने का दूसरा तरीका generate : (a -> msg) -> Generator a -> Cmd msg का उपयोग करना है, लेकिन यह केवल किसी Elm एप्लीकेशन के अंदर ही किया जा सकता है।
ऐसी स्थिति में Elm रनटाइम सीड बनाने के लिए step के साथ-साथ बाहरी और गैर-प्योर संसाधनों का भी उपयोग कर सकता है।
अब से हम जेनरेटर पर ध्यान देंगे।
उदाहरण दिखाने के लिए मान लीजिए कि हमने initialSeed और step की मदद से एक फंक्शन generate : Int -> Generator a -> List a बना लिया है, जो किसी जेनरेटर से कई वैल्यू बनाता है।
Random मॉड्यूल एक तय रेंज के भीतर पूर्णांक और दशमलव संख्याएँ बनाने के लिए दो बुनियादी जेनरेटर देता है:
generate 5 (Random.int -5 5)
--> [0, 3, -5, 5, 0]
generate 3 (Random.float 0 5)
--> [1.61803, 3.14159, 2.71828]
इन वैल्यू को टपल में या कई वैल्यू वाले ऐरे में मिलाया जा सकता है:
generate 2 (Random.list 3 (Random.int 0 3))
--> [[0, 3, 3], [1, 3, 2]]
generate 2 (Random.pair (Random.int 0 3) (Random.float 10 10.3))
--> [(0, 10.23412), (2, 10.17094)]
elm-community/random-extra पैकेज अलग-अलग डेटा स्ट्रक्चर के लिए बहुत सारे और जेनरेटर देता है: स्ट्रिंग, तारीखें, डिक्शनरी, ऐरे, सेट, आदि।
हम Random.constant की मदद से ऐसे जेनरेटर बना सकते हैं जो केवल एक ही वैल्यू लौटाएँगे:
generate 4 (Random.constant "hello")
--> ["hello", "hello", "hello", "hello"]
हम Random.uniform की मदद से दिए गए एलिमेंट में से बराबर संभावना के साथ रैंडम चुनाव कर सकते हैं:
generate 5 (Random.uniform Red [Green, Blue])
--> [Red, Blue, Blue, Green, Red]
Random.uniform दो आर्गुमेंट लेता है (Red और [Green, Blue]), ताकि यह पक्का रहे कि चुनने के लिए कम से कम एक वैल्यू तो हो, क्योंकि एक अकेला ऐरे खाली भी हो सकता है।
हम Random.weighted की मदद से संभावनाएँ भी बदल सकते हैं:
generate 5 (Random.weighted (80, Red) [(15, Green), (5, Blue)])
--> [Red, Red, Green, Red, Red]
इन वैल्यू का जोड़ 100 होना ज़रूरी नहीं है, इन्हें वैसे भी फिर से सामान्य कर दिया जाता है।
हम Random.map की मदद से जेनरेटर के अंदर तक पहुँच सकते हैं:
generate 3 (Random.int 1 6 |> Random.map (\n -> n * 10))
--> [30, 60, 10]
हम Random.map2 से लेकर Random.map5 तक का उपयोग करके और ज़्यादा जेनरेटर मिला सकते हैं:
position =
Random.map3
(\x y z -> Position x y z)
(Random.float -100 100)
(Random.float -100 100)
(Random.float -100 100)
generate 1 position
--> [Position 33.788 -98.321 10.0845]
और जटिल उपयोगों के लिए हमारे पास Random.andThen : (a -> Generator b) -> Generator a -> Generator b है, जो एक जेनरेटर से बनी वैल्यू का उपयोग करके दूसरा जेनरेटर बना सकता है:
bool = Random.uniform True [False]
failHalfOfTheTime : Generator a -> Generator (Maybe a)
failHalfOfTheTime generator =
bool
|> Random.andThen
(\boolResult ->
if boolResult then
Random.map Just generator
else
Random.constant Nothing
)
generate 6 (Random.int 0 1 |> failHalfOfTheTime)
--> [Nothing, Just 1, Just 0, Nothing, Just 1, Nothing]
कभी-कभी किसी जेनरेटर को खुद को दोहराते हुए बनाना काम आता है।
ऐसे मामलों में कंपाइलर जेनरेटर को अनंत बार न खोलता जाए, इसके लिए आपको Random.lazy का उपयोग करना पड़ सकता है।
type Peano
= Zero
| Next Peano
peano : Generator Peano
peano =
Random.uniform (Random.constant Zero)
[ Random.map Next (Random.lazy (\_ -> peano))
]
|> Random.andThen identity
generate 12 peano
--> [Zero, Next(Next(Zero)), Zero, Next(Zero), Zero, Zero, Next(Zero), Zero, Zero, Next(Zero), Next(Next(Next(Zero)))]
यह उदाहरण थोड़ा भारी है, तो चलिए इसे टुकड़ों में समझते हैं।
Peano एक टाइप है जो धनात्मक पूर्णांक दिखाता है: Zero यानी 0, Next(Zero) यानी 1, Next(Next(Zero)) यानी 2, आदि।
हम peano इस तरह बनाते हैं कि यह हमें एक रैंडम Peano संख्या दे।
सबसे पहले ध्यान दीजिए कि Random.lazy तर्क में कुछ नहीं जोड़ता। यह केवल कंपाइलर को peano को peano के अंदर अनंत बार लिखते जाने से रोकता है।
हम Random.uniform का उपयोग करके शून्य (50% संभावना के साथ) और किसी दूसरी Peano संख्या में एक जोड़कर बनी संख्या (50% संभावना के साथ) में से एक चुनते हैं।
लेकिन पिछले उदाहरण के उलट, Random.uniform वैल्यू में से नहीं (जैसे Zero), बल्कि जेनरेटर में से चुन रहा है (जैसे Random.constant Zero), क्योंकि हमें खुद peano का उपयोग करना है।
इसका मतलब है कि Random.uniform Generator (Generator Peano) टाइप की वैल्यू लौटाएगा, जो हमें नहीं चाहिए।
जेनरेटर को "फ्लैट" करने के लिए हम इसे Random.andThen : (a -> Generator b) -> Generator a -> Generator b में भेजते हैं, जहाँ हम चाहते हैं कि b Peano हो।
चूँकि Generator a यानी Generator (Generator Peano) है, इसलिए a को Generator Peano होना चाहिए, और फंक्शन (a -> Generator b) का टाइप (Generator Peano -> Generator Peano) होना चाहिए।
हम कुछ भी बदलना नहीं चाहते, इसलिए identity सही चुनाव है।
अंत में, peano किस तरह की संख्याएँ बनाएगा?
हम जानते हैं कि यह 50% बार 0 बनाएगा, और 50% बार खुद के एक और इटरेशन में एक जोड़कर बनी संख्या बनाएगा।
इसका मतलब है कि संख्याएँ 0 (50% बार), 1 (25% बार), 2 (12% बार), 3 (6% बार), आदि होंगी।
peano मनमाने ढंग से बड़ी संख्याएँ बना सकता है, लेकिन इनकी संभावना घातांकीय रूप से घटती जाती है।
आप एक भूलभुलैया शोधकर्ता हैं, जो कालकोठरियों और रोमांच की जगहों की एक नई पीढ़ी डिज़ाइन करने पर काम कर रहे हैं।
आप एक सिद्धांतकार हैं, यानी भूलभुलैया के भौतिक ढाँचे से आपका कोई लेना-देना नहीं है (वह काम भूलभुलैया इंजीनियरों का है), बल्कि आपको उनकी गणितीय जटिलता में दिलचस्पी है: ब्रांचिंग फैक्टर, अधिकतम गहराई इत्यादि।
आप तय करते हैं कि अपना अगला प्रोजेक्ट यादृच्छिक जनरेशन के तरीके से शुरू करेंगे।
किसी भी अच्छी भूलभुलैया में ज़्यादातर बंद गलियाँ ही होती हैं।
deadend जनरेटर को इस तरह बनाइए कि वह हमेशा DeadEnd वेरिएंट ही जनरेट करे।
चलिए, रोमांच-प्रेमियों को आपकी भूलभुलैयाओं में घुसने की एक वजह देते हैं।
treasure जनरेटर को इस तरह बनाइए कि वह Gold, Diamond या Friendship में से कोई एक समान प्रायिकता के साथ जनरेट करे।
इसके बाद room जनरेटर को इस तरह बनाइए कि वह treasure से जनरेट हुई वैल्यू को Room वेरिएंट में लपेट दे।
और अब आती है भूलभुलैया की जान: शाखाएँ।
branch जनरेटर को एक Generator Maze आर्गुमेंट मिलता है, जो एक ऐसा जनरेटर है जो कोई भी भूलभुलैया जनरेट कर सकता है।
branch जनरेटर को इस तरह बनाइए कि वह Branch वेरिएंट जनरेट करे, जिसमें कई शाखाएँ हों जो उसके आर्गुमेंट से जनरेट हुई भूलभुलैयाओं तक ले जाती हों।
शाखाओं की संख्या 2 से 4 के बीच होनी चाहिए (इससे ज़्यादा हुई तो भूलभुलैया इंजीनियर को गुस्सा आ जाएगा)। यह संख्या समान प्रायिकता के साथ यादृच्छिक रूप से तय होनी चाहिए।
अब वास्तविक काम शुरू करने का समय है।
maze जनरेटर को इस तरह बनाइए कि वह 60% बार बंद गली, 15% बार खजाने वाला कमरा और 25% बार शाखा जनरेट करे।
शाखाओं को maze का पुनरावर्ती रूप से उपयोग करके नई भूलभुलैयाएँ जनरेट करनी चाहिए।
जनरेटर में deadend, room और branch का उपयोग करना न भूलिए।
हर शाखा में औसतन 3 भूलभुलैयाें होती हैं। इनमें से सिर्फ 25% ही फिर से शाखाएँ बनाएँगी, यानी औसतन एक से भी कम। इसलिए आपकी जनरेट की गई भूलभुलैयाएँ अंततः खत्म हो जाएँगी।
आपका maze अच्छा है, लेकिन उसमें कुछ दिक्कतें हैं:
MazeMakerSupport.mazeDepth से निकाली जाती है)mazeOfDepth n जनरेटर को इस तरह बनाइए कि वह गहराई n वाली भूलभुलैया जनरेट करे।
सिर्फ सबसे गहरे लेवल 0 में बंद गलियाँ या खजाने वाले कमरे होने चाहिए (समान प्रायिकता के साथ)। बाकी सभी लेवल एक शाखा होने चाहिए, जिसमें mazeOfDepth से पुनरावर्ती रूप से जनरेट की गई गहराई n-1 वाली भूलभुलैयाें हों।
जनरेटर में deadend, room और branch का उपयोग करना न भूलिए।
Exercism पर साइन अप कीजिए और Elm को 28 कॉन्सेप्ट110 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।