पिछली बार, हमने एक वैज्ञानिक कैलकुलेटर बनाया था और इसका उपयोग चर और प्रकारों के साथ सहज होने के लिए किया था। अब हम एक संख्या अनुमान लगाने वाला खेल बनाने जा रहे हैं और सीखेंगे कि कैसे हमारा कार्यक्रम आपके जीवन में खेले गए खेलों में पुन: प्रयास तंत्र और कुछ शर्तों के आधार पर निर्णय लेने के तरीके की तरह खुद को दोहरा सकता है।
इस गेम में, हमारा प्रोग्राम एक संख्या तय करेगा और फिर हमें कई बार पुनः प्रयास में उस संख्या का अनुमान लगाना होगा। एक बार जब हम किसी संख्या का अनुमान लगा लेते हैं, तो प्रोग्राम उत्तर देगा कि क्या हमारा अनुमान सही है या हमारी अनुमानित संख्या वास्तविक संख्या से अधिक या कम है और हम फिर से प्रयास करेंगे।
हम संख्याओं की तुलना करने के लिए if का उपयोग करेंगे, गेम को चालू रखने के लिए एक लूप, इनपुट से निपटने के लिए match, और break या continue का उपयोग करेंगे जब हमें यह बदलने की आवश्यकता होगी कि लूप आगे क्या करता है।
प्रवाह को नियंत्रित करें
आइए नियंत्रण प्रवाह के बारे में समझने से शुरुआत करें। बिना किसी शाखा वाला प्रोग्राम एक के बाद एक स्टेटमेंट चलाता है। हमारे पहले लेख में, हमारे प्रोग्राम की कोई शाखा नहीं थी, यह क्रमबद्ध तरीके से चल रहा था या निष्पादित हो रहा था। आपको एक उदाहरण देता हूँ
fn main() {
let a = 10;
let b = 20;
let result = a + b;
println!("{result}");
}लेकिन हमारे अनुमान लगाने के खेल के लिए, हमें प्रोग्राम लिखने का एक तरीका चाहिए ताकि यह कुछ शर्तों के आधार पर कार्रवाई कर सके और इसमें पुनः प्रयास लूप भी हो सके। इन चीजों को प्राप्त करने के लिए, हम न केवल रस्ट में बल्कि किसी भी प्रोग्रामिंग भाषा में नियंत्रण प्रवाह अवधारणा का उपयोग करते हैं। तो, आइए शर्तों की अवधारणा से शुरुआत करें और फिर हम इस लेख में बाद में लूप के बारे में सीखेंगे।
if
रस्ट में, if सिंटैक्स अन्य प्रोग्रामिंग भाषाओं के समान दिखता है लेकिन अभी भी कुछ अंतर हैं
fn main() {
let number = 10;
if number > 5 {
println!("Number is greater than 5");
}
}इस प्रोग्राम में, हम पहले वेरिएबल number को 10 के मान से आरंभ कर रहे हैं और फिर हम if का उपयोग करके एक शर्त लगा रहे हैं। शर्त यह है कि यदि शर्त number > 5 सत्य या संतुष्ट है, तो आगे बढ़ें और “संख्या 5 से अधिक है” स्ट्रिंग प्रिंट करें। अन्य भाषाओं की तरह, यदि यह शर्त संतुष्ट नहीं है, तो यह उस if ब्लॉक के अंदर जो कुछ भी है उसे निष्पादित नहीं करेगा, इसलिए हमारा प्रोग्राम कुछ भी प्रिंट नहीं करेगा और फिर प्रोग्राम से बाहर निकल जाएगा।
स्थितियाँ बूलियन होनी चाहिए
कुछ अन्य भाषाओं के विपरीत, रस्ट में सत्य और मिथ्या मूल्य नहीं हैं। आइए मैं आपको दिखाता हूं कि इससे मेरा क्या मतलब है:
fn main() {
let number = 10;
if number {
println!("Number exists");
}
}यदि आप इसे चलाएंगे, तो आप देखेंगे कि कंपाइलर यह कहते हुए शिकायत करेगा कि अपेक्षित बूल लेकिन पूर्णांक मिला। याद रखें, मैंने पहले लेख में क्या कहा था, if स्थिति को एक bool उत्पन्न करना चाहिए और अभी number वास्तव में एक i32 पूर्णांक है, हमें एक शर्त लगाने की आवश्यकता है ताकि रस्ट उस स्थिति का मूल्यांकन कर सके और फिर स्थिति एक बूल मान या तो सही या गलत लौटा देगी। तो, इसे ठीक करने के लिए, आप यह कर सकते हैं:
fn main() {
let number = 10;
if number != 0 {
println!("Number is not zero");
}
}आपको इसे याद रखने की आवश्यकता है क्योंकि कुछ अन्य भाषाओं में सत्य और मिथ्या मूल्यों की अवधारणा का उपयोग करना बहुत आम है। मैं कहूंगा, यह रस्ट की ओर से एक अच्छी बात है क्योंकि bool की आवश्यकता नियमों को रोकती है जैसे कि 0 का गलत होना या एक गैर-रिक्त स्ट्रिंग का सत्य होना या एक शून्य संदर्भ का गलत होना इत्यादि।
else और else if
जैसा कि आप नाम से अनुमान लगा सकते हैं, हम शेष मामलों के लिए else का उपयोग करेंगे
fn main() {
let number = 3;
if number > 5 {
println!("Number is greater than 5");
} else {
println!("Number is 5 or less");
}
}अन्य भाषाओं की तरह, else ब्लॉक वैकल्पिक है लेकिन यदि आप कभी भी else ब्लॉक का उपयोग करना चाहते हैं तो आपको if ब्लॉक का उपयोग करना होगा। else ब्लॉक में, हमें कोई शर्त देने की आवश्यकता नहीं है क्योंकि हम मूल रूप से शेष सभी मामलों के लिए कह रहे हैं, अन्य भाषाओं की तरह ही else ब्लॉक में जो कुछ भी है उसे निष्पादित करें।
अब, else if के लिए (हाँ, आपने सही अनुमान लगाया), इसका उपयोग तब किया जाता है जब अधिक मामले हों
fn main() {
let number = 10;
if number > 10 {
println!("Number is greater than 10");
} else if number == 10 {
println!("Number is exactly 10");
} else {
println!("Number is less than 10");
}
}यह फिर से अन्य भाषाओं के समान है, हम कई शर्तों की जांच कर रहे हैं, यदि एक शर्त संतुष्ट होती है, तो बाकी शर्तें बिल्कुल भी निष्पादित नहीं होंगी। हमारा खेल भी इसी संरचना का उपयोग करेगा।
if एक अभिव्यक्ति है
ध्यान देने योग्य एक महत्वपूर्ण बात यह है कि रस्ट में, if एक मान उत्पन्न कर सकता है
fn main() {
let number = 10;
let message = if number > 5 {
"greater than 5"
} else {
"5 or less"
};
println!("{message}");
}लूप्स
अब, रस्ट में लूप्स को समझते हैं। रस्ट में, तीन सामान्य लूप फॉर्म हैं: loop, while और for। अन्य भाषाओं की तरह, लूप का उपयोग किसी ब्लॉक को एक निश्चित समय तक दोहराने के लिए किया जाता है।
loop
आइए loop से शुरुआत करें। loop ने इसके सिंटैक्स में कोई शर्त नहीं रखी है, यह तब तक दोहराया जाता है जब तक कि निष्पादन इसे break, return, एक पैनिक या किसी डायवर्जिंग ऑपरेशन के माध्यम से नहीं छोड़ देता है। आइए मैं आपको दिखाता हूं कि इससे मेरा क्या मतलब है
fn main() {
loop {
println!("Hello");
}
}तो, आप देख सकते हैं कि यह लूप एक अनंत लूप है, आपको इसे Ctrl + C से मैन्युअल रूप से रोकना होगा। एक लूप जो कभी ख़त्म नहीं हो सकता, वह रस्ट के टाइप मॉडल में कभी भी ! टाइप नहीं करता है। नेवर प्रकार एक अभिव्यक्ति का प्रतिनिधित्व करता है जो सामान्य मान उत्पन्न नहीं करता है क्योंकि निष्पादन इसके बाद जारी नहीं रहता है।
तो, इस अनंत लूप सिस्टम को रोकने के लिए हम break का उपयोग कर सकते हैं, break निकटतम संलग्न लूप से बाहर निकलता है और उस लूप के बाद निष्पादन फिर से शुरू होता है। यह return से भिन्न है, return पूरे फ़ंक्शन से बाहर निकल जाता है लेकिन break बस वर्तमान ब्लॉक से बाहर हो जाता है।
fn main() {
let mut number = 1;
loop {
println!("{number}");
if number == 5 {
break;
}
number += 1;
}
}हम लूप के अंदर संख्या प्रिंट कर रहे हैं लेकिन आप देख सकते हैं कि एक if शर्त है जो कहती है कि यदि number == 5 है तो इस लूप से बाहर निकलें
continue भी है, हम इसका उपयोग तब करते हैं जब वर्तमान पुनरावृत्ति समाप्त होनी चाहिए लेकिन लूप स्वयं चालू रहना चाहिए
fn main() {
let mut number = 0;
loop {
number += 1;
if number == 3 {
continue;
}
println!("{number}");
if number == 5 {
break;
}
}
}दोबारा, हम लूप के अंदर number वैरिएबल को बढ़ा रहे हैं, फिर एक शर्त है जो कहती है कि यदि number == 3, तो continue इसका मतलब है कि इस विशिष्ट पुनरावृत्ति के बाकी हिस्सों को छोड़ दें और नियंत्रण को लूप सीमा पर वापस स्थानांतरित करें।
तो, यह 1, 2, 4 और 5 को प्रिंट करेगा। यह 3 को छोड़ देगा क्योंकि यदि आप इसे ड्राई रन करेंगे, तो पहले संख्या 0 है, फिर हम लूप में आते हैं और संख्या मान को 1 तक बढ़ाते हैं, फिर यदि स्थिति विफल हो जाती है, तो हम इसे छोड़ देते हैं, फिर हम संख्या को प्रिंट करते हैं, इसलिए यह 1 प्रिंट करता है, फिर यदि चेक विफल हो जाता है तो एक और है, अब यह फिर से लूप पर वापस जाता है, उसी तरह यह 2 को प्रिंट करेगा, अब यह वापस शीर्ष पर जाता है लूप, और मान को 3 तक बढ़ा देता है, इसके बाद यदि चेक संतुष्ट होता है और यह कहता है continue, इसका मतलब है कि इस पुनरावृत्ति के लिए शेष पंक्तियों को छोड़ दें और लूप के शीर्ष पर वापस जाएं, फिर मान 4 तक बढ़ जाता है और 4 प्रिंट करता है, फिर यह 5 प्रिंट करता है लेकिन इस पुनरावृत्ति के अंत में, अंतिम यदि चेक संतुष्ट होता है और break निष्पादित होता है और हम उससे बाहर निकलते हैं पाश.
एक बात का ध्यान रखें कि break और continue दोनों निकटतम लूप पर कार्य करते हैं जब तक कि लूप लेबल प्रदान नहीं किया जाता है (मुझे लगता है कि हमारे पास पायथन में ऐसा कुछ है लेकिन निश्चित नहीं है)। जब लूप्स नेस्टेड होते हैं तो लेबल उपयोगी होते हैं:
fn main() {
'outer: loop {
loop {
break 'outer;
}
}
}एपोस्ट्रोफ लेबल की शुरुआत करता है; यह एक स्ट्रिंग नहीं है. break 'outer सीधे बाहरी लूप से बाहर निकलता है। अनुमान लगाने के खेल में केवल एक लूप होता है, इसलिए इसे किसी लेबल की आवश्यकता नहीं होती है। मैं कहूंगा कि यह एक बढ़िया अतिरिक्त है क्योंकि मैंने ऐसी स्थितियों का सामना किया है जहां आंतरिक लूप के अंदर मैं एक शर्त रखना चाहता हूं और यदि वह स्थिति संतुष्ट होती है तो मैं आंतरिक और बाहरी दोनों लूप से बाहर निकलना चाहता हूं, उस स्थिति में इस तरह की सुविधा वास्तव में मदद करती है।
while
अब आइए लूप के दूसरे रूप को देखें जिसे while कहा जाता है। while प्रत्येक पुनरावृत्ति से पहले इसकी स्थिति की जांच करता है, यह अन्य भाषाओं के समान है:
fn main() {
let mut number = 1;
while number <= 5 {
println!("{number}");
number += 1;
}
}हमारे पास मान 1 के साथ एक number वैरिएबल है, फिर हम लूप में प्रवेश कर रहे हैं और स्थिति की जांच होती है, यदि स्थिति संतुष्ट होती है तो कोड ब्लॉक निष्पादित होगा और यदि नहीं तो हम उस लूप से बाहर निकल जाएंगे। तो यह प्रोग्राम नंबर 1,2,3,4,5 प्रिंट करेगा, फिर यह टूट जाएगा क्योंकि शर्त अब पूरी नहीं होगी।
एक तरह से, while लूप एक loop की तरह कार्य करता है जिसमें एक if होता है जो break को कॉल करता है जब स्थिति false होती है।
for
for लूप एक पुनरावर्तक से मान लेता है
fn main() {
for number in 1..=5 {
println!("{number}");
}
}श्रेणी 1..=5 में दोनों समापन बिंदु शामिल हैं। प्रत्येक पुनरावृत्ति पर, अगला आइटम number से जुड़ा होता है और बॉडी उस आइटम के लिए एक बार चलती है। यदि आप पुनरावृत्ति को 1 से 5 (समावेशी नहीं) तक चलाना चाहते हैं, तो आप केवल बराबर के बिना 1..5 का उपयोग कर सकते हैं। अधिक सटीक रूप से, for किसी भी मान को स्वीकार करता है जो IntoIterator को लागू करता है, इसे एक पुनरावर्तक में परिवर्तित करता है, बार-बार next को कॉल करता है, और जब next वापस आता है तो रुक जाता है None। for के बाद का वेरिएबल तकनीकी रूप से एक पैटर्न है, इसलिए यह वस्तुओं के उत्पादन के दौरान उन्हें नष्ट कर सकता है। रस्ट का मानक for सिंटैक्स मैन्युअल इंडेक्सिंग और सीमा जांच से भी बचता है जो आमतौर पर सी-स्टाइल लूप वाली भाषाओं में संग्रह को पार करने के लिए उपयोग किया जाता है। हम बाद में पुनरावृत्तियों और पैटर्न को गहराई से कवर करेंगे।
match
match एक मान का मूल्यांकन करता है, जिसे स्क्रूटनी कहा जाता है और इसकी तुलना पैटर्न के एक सेट से करता है
fn main() {
let number = 2;
match number {
1 => println!("One"),
2 => println!("Two"),
3 => println!("Three"),
_ => println!("Something else"),
}
}match ब्लॉक के अंदर की प्रत्येक पंक्ति या अनुभाग को एक भुजा कहा जाता है, प्रत्येक भुजा के बाईं ओर => का एक पैटर्न होता है और दाईं ओर उस पैटर्न के लिए अभिव्यक्ति होती है। रस्ट ऊपर से नीचे तक भुजाओं का परीक्षण करता है और पहले मिलान वाली भुजाओं को चलाता है। चूँकि number 2 है, दूसरा हाथ चलता है
_ पैटर्न एक वाइल्डकार्ड है जो हर उस मान को संभालता है जो पहले वाले आर्म से मेल नहीं खाता है। यदि आप इसे हटा देंगे, तो कंपाइलर शिकायत करेगा क्योंकि रस्ट को प्रोग्राम को संकलित करने से पहले हर संभावित मामले को कवर करने की आवश्यकता होती है। यह उन चीज़ों में से एक है जो आपको याद रखनी चाहिए कि जब आप match का उपयोग कर रहे हैं, तो आपको सभी संभावित मानों को कवर करना चाहिए अन्यथा यह संकलित नहीं होगा।
पैटर्न शाब्दिक मूल्यों की तुलना करने के अलावा और भी बहुत कुछ कर सकते हैं। वे नामों को बांध सकते हैं, टुपल्स और स्ट्रक्चर को नष्ट कर सकते हैं, एनम वेरिएंट का चयन कर सकते हैं, मूल्य के कुछ हिस्सों को अनदेखा कर सकते हैं और श्रेणियां शामिल कर सकते हैं। इस लेख में हमें केवल शाब्दिक पैटर्न, वाइल्डकार्ड और Ok और Err एनम पैटर्न की आवश्यकता है।
बिल्कुल if की तरह, match एक अभिव्यक्ति है और एक मान उत्पन्न कर सकता है
fn main() {
let number = 2;
let message = match number {
1 => "One",
2 => "Two",
3 => "Three",
_ => "Something else",
};
println!("{message}");
}आप देख सकते हैं कि number का मान 2 है, इसलिए संबंधित भुजा मेल खाएगी और message "Two" को इंगित करेगा, इसलिए जब हम message प्रिंट करेंगे, तो यह "Two" प्रिंट होगा
आपको एक बात का ध्यान रखना होगा कि अभिव्यक्ति के रूप में match का उपयोग करते समय सुनिश्चित करें कि सभी भुजाओं को संगत प्रकार का उत्पादन करना चाहिए
match आपको switch की याद दिला सकता है, लेकिन संपूर्णता, अभिव्यक्ति मूल्य और विध्वंस इसे उन भाषाओं में बीजीय पैटर्न मिलान के करीब बनाते हैं जो टैग किए गए यूनियनों का समर्थन करते हैं। हम यहां पार्सिंग इनपुट के दो संभावित परिणामों को संभालने के लिए इसका उपयोग करेंगे।
Result
पिछले आलेख में, हमने देखा कि जब हमें कमांड लाइन के माध्यम से उपयोगकर्ता से इनपुट मिलता है, तो हमें वह एक स्ट्रिंग के रूप में मिलता है और कभी-कभी हमें उनके प्रकार को तदनुसार परिवर्तित करने की आवश्यकता होती है। हमारे अनुमान प्रोजेक्ट के लिए, हमें इनपुट को एक संख्या में बदलने की आवश्यकता है
fn main() {
let number = "42".parse::<u32>();
println!("{number:?}");
}यदि आप इसे प्रिंट करेंगे, तो आउटपुट Ok(42) होगा। यदि स्ट्रिंग “हैलो” है, तो आउटपुट Err(ParseIntError {...}) जैसा दिखेगा। यहां क्या हो रहा है कि पार्सिंग रिटर्न Result<T, E> देता है क्योंकि ऑपरेशन सफल या विफल हो सकता है (हमने अपने उदाहरण से दोनों मामलों को देखा)। T सफलता प्रकार है और E त्रुटि प्रकार है। इसकी परिभाषा का एक सरलीकृत संस्करण इस तरह दिखता है
enum Result<T, E> {
Ok(T),
Err(E),
}प्रत्येक Result मान में बिल्कुल उन वेरिएंट्स में से एक होता है Ok(value) या Err(error)
T और E सामान्य पैरामीटर हैं, इसलिए अलग-अलग ऑपरेशन अलग-अलग सफलता और त्रुटि प्रकारों के साथ एक ही Result संरचना का उपयोग कर सकते हैं। "42".parse::<u32>() के लिए, सफलता प्रकार u32 है और त्रुटि प्रकार ParseIntError है। ::<u32> सिंटैक्स को आमतौर पर टर्बोफिश कहा जाता है; यह सामान्य parse विधि बताता है कि हम कौन सा लक्ष्य प्रकार चाहते हैं जब आसपास का कोड पर्याप्त जानकारी प्रदान नहीं करता है।
हम एनम और जेनेरिक को अलग से कवर करेंगे, लेकिन हम पहले से ही match के साथ वेरिएंट को संभाल सकते हैं:
fn main() {
let result = "42".parse::<u32>();
match result {
Ok(number) => println!("Parsed number: {number}"),
Err(error) => println!("Failed to parse: {error}"),
}
}पैटर्न Ok(number) दोनों प्रकार की जांच करता है और उसके निहित मूल्य को number से जोड़ता है, जबकि Err(error) त्रुटि के लिए भी ऐसा ही करता है। यह मूल्य प्राप्त करने के लिए एक अलग स्थिति जांच के बाद दूसरी कॉल के बजाय विनाशकारी है।
आपने पहले रस्ट कोड में unwrap() का उपयोग किया होगा
let number = "42".parse::<u32>().unwrap();unwrap() Ok के अंदर मान लौटाता है। इसे Err पर कॉल करने से घबराहट होती है
fn main() {
let number = "hello".parse::<u32>().unwrap();
println!("{number}");
}unwrap() तब सुविधाजनक होता है जब निर्माण में विफलता असंभव होती है, त्वरित प्रयोगों के दौरान, या जब घबराहट अपेक्षित प्रतिक्रिया होती है। उपयोगकर्ता इनपुट में गलतियाँ होने की उम्मीद है, इसलिए घबराहट सामान्य इनपुट को प्रोग्राम क्रैश में बदल देगी। हमारा गेम match के साथ पार्सिंग परिणाम को संभालेगा और चलता रहेगा।
संख्या अनुमान लगाने का खेल बनाना
ठीक है, बहुत हो गया सीखना, अब अपना प्रोजेक्ट बनाना शुरू करते हैं
प्रोजेक्ट बनाएं
आइए पहले एक कार्गो प्रोजेक्ट बनाएं और इसे अपनी पसंदीदा आईडीई में खोलें
cargo new number-guessing-gameअब, आप प्रक्रिया जानते हैं, src/main.rs खोलें और मौजूदा हैलो वर्ल्ड कोड हटा दें
यादृच्छिक संख्या सृजन जोड़ें
मानक लाइब्रेरी में यहां आवश्यक यादृच्छिक संख्या फ़ंक्शन शामिल नहीं है, इसलिए rand क्रेट को Cargo.toml में जोड़ें। कार्गो इस मैनिफेस्ट को पढ़ता है, एक संगत क्रेट संस्करण को हल करता है, इसके स्रोत को डाउनलोड करता है, और Cargo.lock में सटीक हल किए गए निर्भरता ग्राफ को रिकॉर्ड करता है।
[package]
name = "number-guessing-game"
version = "0.1.0"
edition = "2024"
[dependencies]
rand = "0.10"ठीक है, अब हमारे पास rand क्रेट है, आइए समावेशी श्रेणी 1..=100 से एक संख्या उत्पन्न करके शुरुआत करें
fn main() {
let secret_number = rand::random_range(1..=100);
println!("Secret number: {secret_number}");
}random_range श्रेणी के मान प्रकार पर सामान्य है। इस पृथक उदाहरण में, बिना प्रत्यय वाले पूर्णांक अक्षर डिफ़ॉल्ट रूप से i32 होते हैं, इसलिए secret_number को i32 के रूप में अनुमानित किया जाता है। अंतिम कार्यक्रम में, पार्स किए गए u32 के साथ इसकी तुलना करने से कंपाइलर को इसके बजाय u32 का अनुमान लगाने के लिए पर्याप्त संदर्भ मिलता है। हम let secret_number: u32 = ... के साथ किसी भी स्तर पर प्रकार को स्पष्ट कर सकते हैं।
..= सिंटैक्स एक समावेशी श्रेणी का निर्माण करता है, इसलिए 1 और 100 दोनों संभावित परिणाम हैं। तुलनात्मक रूप से, 1..100 100 को बाहर कर देगा। प्रोग्राम को कई बार चलाएँ और पुष्टि करें कि मान बदल गया है। println! अस्थायी है और इस जाँच के बाद हटा दिया जाएगा।
इनपुट पढ़ें
अब जब हम जानते हैं कि एक यादृच्छिक संख्या कैसे उत्पन्न की जाती है जिसका उपयोगकर्ता को अनुमान लगाना होता है, तो आइए अब उपयोगकर्ता से अनुमानित संख्या लें
use std::io;
fn main() {
let secret_number = rand::random_range(1..=100);
println!("I'm thinking of a number between 1 and 100.");
println!("Enter your guess:");
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
println!("You entered: {input}");
println!("Secret number: {secret_number}");
}String::new() एक स्वामित्व वाली, बढ़ने योग्य UTF-8 स्ट्रिंग बनाता है। read_line मानक इनपुट से पढ़े गए बाइट्स को उस स्ट्रिंग में जोड़ता है, यही कारण है कि इसे अपरिवर्तनीय संदर्भ के बजाय &mut input की आवश्यकता होती है। एक परिवर्तनीय संदर्भ को पारित करने से read_line को स्ट्रिंग का स्वामित्व लिए बिना उसे बदलने की अनुमति मिलती है, इसलिए main बाद में भी input का उपयोग कर सकता है।
विधि io::Result<usize> लौटाती है। इसका सफलता मान पढ़े गए बाइट्स की संख्या है, जबकि इसका त्रुटि मान इनपुट/आउटपुट विफलता का वर्णन करता है। यह ड्राफ्ट निचले स्तर की विफलता के लिए unwrap() का उपयोग करता है ताकि लेख नियंत्रण प्रवाह और सामान्य अमान्य अनुमानों पर केंद्रित रह सके। एक उत्पादन कार्यक्रम इसे Result भी संभाल सकता है।
अनुमान को पार्स करें
ठीक है, अब जब हमें उपयोगकर्ता द्वारा अनुमान लगाया गया मान प्राप्त हो गया है, तो इसे पार्स करने और इसे एक संख्या में परिवर्तित करने का समय आ गया है
वर्तमान में, input एक String है और secret_number एक संख्या है, इसलिए हम इन दो मानों की सीधे तुलना नहीं कर सकते हैं, हमें पहले अनुमानित संख्या को u32 के रूप में परिवर्तित करने की आवश्यकता है (हम i32 के लिए भी जा सकते हैं लेकिन क्योंकि हमारी गुप्त संख्या हमेशा सकारात्मक होगी, u32 उपयोग में अच्छा है)
let guess = input.trim().parse::<u32>();read_line जब उपयोगकर्ता एंटर दबाता है तो सामान्यतः नई लाइन बनी रहती है। trim() अग्रणी और अनुगामी रिक्त स्थान को हटाकर एक स्ट्रिंग स्लाइस लौटाता है, फिर parse::<u32>() उस स्लाइस से एक संख्या बनाने का प्रयास करता है। लौटाए गए Result को संभालने के लिए match का उपयोग करें:
```rust
use std::io;
fn main() {
let secret_number = rand::random_range(1..=100);
println!("I'm thinking of a number between 1 and 100.");
println!("Enter your guess:");
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let guess = match input.trim().parse::<u32>() {
Ok(number) => number,
Err(error) => {
println!("Invalid input: {error}");
return;
}
};
println!("Your guess: {guess}");
println!("Secret number: {secret_number}");
}संपूर्ण match अभिव्यक्ति guess को निर्दिष्ट मान बन जाती है। Ok आर्म एक u32 उत्पन्न करता है, जबकि Err आर्म त्रुटि प्रिंट करता है और return निष्पादित करता है। हालाँकि इन भुजाओं के अलग-अलग प्रकार प्रतीत होते हैं, return का प्रकार कभी नहीं होता है ! क्योंकि यह वर्तमान फ़ंक्शन में कोई मान उत्पन्न नहीं करता है। एक अपसारी अभिव्यक्ति वहां फिट हो सकती है जहां किसी अन्य प्रकार की आवश्यकता होती है, इसलिए पूर्ण मिलान का प्रकार u32 है।
यह पहला संस्करण return का उपयोग करता है, जो main से बाहर निकलता है और इसलिए प्रोग्राम समाप्त होता है। अगले संस्करण को एक अलग नियंत्रण-प्रवाह लक्ष्य की आवश्यकता है क्योंकि अमान्य पाठ केवल वर्तमान प्रयास को समाप्त करना चाहिए।
पुनः प्रयास जोड़ें
अब जब हमने उपयोगकर्ता से अनुमानित संख्या को सही ढंग से पार्स कर लिया है, तो पुनः प्रयास तंत्र को जोड़ने का समय आ गया है ताकि उपयोगकर्ता तब तक प्रयास करना जारी रख सके जब तक कि वे इसे सही न कर लें।
use std::io;
fn main() {
let secret_number = rand::random_range(1..=100);
println!("I'm thinking of a number between 1 and 100.");
loop {
println!("Enter your guess:");
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let guess = match input.trim().parse::<u32>() {
Ok(number) => number,
Err(error) => {
println!("Invalid input: {error}");
continue;
}
};
println!("Your guess: {guess}");
}
}hello दर्ज करने पर अब एक त्रुटि प्रिंट होती है और प्रॉम्प्ट पर वापस आ जाता है। जैसे return, continue एक अपसारी अभिव्यक्ति है जिसका प्रकार ! है, इसलिए Err भुजा को Ok भुजा से अपेक्षित u32 उत्पन्न करने की आवश्यकता नहीं है। guess बनने से पहले यह नियंत्रण को अगले पुनरावृत्ति पर स्थानांतरित करता है। एक वैध संख्या अंतिम println! तक पहुंचती है।
अनुमान की तुलना करें
अब, आइए उपयोगकर्ता की अनुमानित संख्या और गुप्त संख्या की तुलना करें, और हम उन्हें बता सकते हैं कि क्या वे सही हैं या उनका अनुमान बहुत कम या बहुत अधिक है। अंतिम println! को तुलना से बदलें
if guess < secret_number {
println!("Too low!");
} else if guess > secret_number {
println!("Too high!");
} else {
println!("You got it!");
}स्थितियाँ परस्पर अनन्य हैं। किन्हीं दो क्रमित पूर्णांकों के लिए, अनुमान गुप्त संख्या से कम, उससे अधिक या उसके बराबर होना चाहिए। इसलिए अंतिम else तुलना को दोहराए बिना समानता का प्रतिनिधित्व करता है।
प्रोग्राम अब सही संकेत दे सकता है, लेकिन सही अनुमान के बाद भी लूप जारी रहता है। break को अंतिम शाखा में जोड़ें। यह सादा break पूरे लूप का मूल्यांकन () पर करता है, और लूप के बाद निष्पादन फिर से शुरू होता है। चूँकि main में लूप अंतिम ऑपरेशन है, फ़ंक्शन समाप्त हो जाता है
संपूर्ण कार्यक्रम
आपके संदर्भ के लिए पूरा कोड यहां दिया गया है
use std::io;
fn main() {
let secret_number = rand::random_range(1..=100);
println!("I'm thinking of a number between 1 and 100.");
loop {
println!("Enter your guess:");
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let guess = match input.trim().parse::<u32>() {
Ok(number) => number,
Err(error) => {
println!("Invalid input: {error}");
continue;
}
};
if guess < secret_number {
println!("Too low!");
} else if guess > secret_number {
println!("Too high!");
} else {
println!("You got it!");
break;
}
}
}यदि आप इसे अभी चलाएंगे और गेम खेलेंगे, तो आपके पास कुछ ऐसा हो सकता है:
I'm thinking of a number between 1 and 100.
Enter your guess:
50
Too low!
Enter your guess:
75
Too high!
Enter your guess:
68
You got it!व्यायाम
इस लेख से, मैंने आपको कुछ व्यायाम चुनौतियाँ देने के बारे में सोचा, जिन्हें आप जोड़ सकते हैं और कार्यक्रम में सुधार कर सकते हैं, इस तरह आप कुछ अभ्यास भी प्राप्त कर सकते हैं:
वैध प्रयासों की गणना करें
एक परिवर्तनीय काउंटर जोड़ें और प्रत्येक वैध अनुमान के बाद इसे बढ़ाएं। खिलाड़ी के जीतने पर गिनती प्रिंट करें:
You got it in 7 attempts!पार्सिंग के बाद वेतन वृद्धि रखने का मतलब है कि अमान्य पाठ को प्रयास के रूप में नहीं गिना जाएगा। दो अमान्य इनपुट और उसके बाद तीन मान्य अनुमानों के साथ प्रोग्राम का परीक्षण करें; रिपोर्ट की गई संख्या 3 होनी चाहिए।
सीमा सत्यापित करें
पार्सिंग और सत्यापन अलग-अलग चरण हैं। 500 एक वैध u32 है, इसलिए संख्या गेम की सीमा से बाहर होने पर भी पार्सिंग सफल होती है। पार्सिंग के बाद एक if शर्त जोड़ें और जब अनुमान 1..=100 से बाहर हो तो continue का उपयोग करें। सत्यापित करें कि 0, 101, और 500 खारिज कर दिए गए हैं जबकि 1 और 100 तुलना तक पहुंच गए हैं।
loop का उपयोग करके while को फिर से लिखें
पिछला while number <= 5 उदाहरण लें और loop, if, और break के साथ समान व्यवहार व्यक्त करें। दोनों प्रोग्रामों को 1 से 5 तक के नंबर प्रिंट करने चाहिए। यह अभ्यास स्पष्ट निकास के साथ सशर्त लूप और बिना शर्त लूप के बीच संबंध की जांच करता है।
लूप से एक मान लौटाएँ
एक लूप लिखें जो ऊपर की ओर गिना जाता है और break number; का उपयोग करता है जब number * number 100 से बड़ा होता है। लूप का मान संग्रहीत करें और उसे प्रिंट करें। परिणाम 11 होना चाहिए।
ठीक है, फिर, मुझे लगता है कि आपने इस लेख में कुछ अच्छी चीजें सीखी हैं, मैं मानता हूं कि परियोजना सरल थी, लेकिन चिंता न करें, 5 वें या 6 वें लेख से, परियोजनाएं तेजी से चुनौतीपूर्ण और दिलचस्प होने जा रही हैं, तब तक हमें रस्ट की मूल बातें सीखनी होंगी।
अगले लेख में, हम फ़ंक्शंस और अभिव्यक्तियों को कवर करेंगे और एक तापमान कनवर्टर का निर्माण करेंगे (मुझे पता है कि यह सरल है लेकिन बहुत अधिक संज्ञानात्मक भार के बिना अवधारणाओं को तेजी से सीखने के लिए सरल परियोजनाएं वास्तव में अच्छी हैं)। मैं जल्द ही आपसे मिलूंगा, तब तक आपका जीवन मंगलमय हो!