Computer >> कंप्यूटर ट्यूटोरियल >  >> प्रोग्रामिंग >> Ruby

रूबी में निल (nil) के जाल से बचना: विश्वसनीय कोड के लिए सर्वोत्तम अभ्यास

पिछले छह महीनों से मैं रस्ट में NES एमुलेटर पर काम कर रहा हूँ। जैसा कि आप अनुमान लगा सकते हैं, मैंने रस्ट के बारे में बहुत कुछ सीखा है, और NES के आंतरिक कामकाज के बारे में तो और भी ज्यादा। लेकिन इस अनुभव ने रूबी को देखने का मेरा नजरिया भी बदल दिया है।

खासकर, इसने मुझे उन मेथड्स के बारे में बेहद सतर्क कर दिया है जो nil लौटाते हैं।

यदि आप किसी चीज़ के लिए खड़े नहीं होते, तो आप किसी भी चीज़ के लिए गिर जाएंगे

रूबी में nil का क्या अर्थ है? लगभग कुछ भी। जब कोई मेथड nil लौटाता है, तो इसका मतलब हो सकता है:

  • मेथड का कोई रिटर्न वैल्यू नहीं है
  • आमतौर पर एक रिटर्न वैल्यू होता है लेकिन इस बार नहीं
  • यह डेटाबेस से एक वैल्यू लौटाता है, जो NULL है
  • कुछ अप्रत्याशित हुआ है

इससे कोड पढ़ना मुश्किल हो जाता है और यह रूबी में सबसे आम अपवाद NoMethodError का मुख्य कारण है। एक एक्सेप्शन मॉनिटरिंग सर्विस के पार्ट-ओनर के रूप में, NoMethodError मेरे बच्चे की स्कूल फीस भर रहा है।

निम्नलिखित कोड देखें। यह ज्यादातर समय nil लौटाता है क्योंकि if स्टेटमेंट्स nil का मूल्यांकन करते हैं जब कंडीशनल मेल नहीं खाता और कोई else नहीं होता।

def color_name(rgb)
  if rgb == 0x000000
    "black"
  end
end

color_name("#FFFFFF").titleize
# => NoMethodError: undefined method `titleize' for nil:NilClass

यदि आप एक अनुभवी रूबी डेवलपर हैं, तो आप जानते हैं कि यह रैबिट होल इससे कहीं गहरा जाता है। कभी-कभी nil के ये अलग-अलग अर्थ अजीब तरीके से ओवरलैप होते हैं, जिससे यह जानना असंभव हो जाता है कि - उदाहरण के लिए - डेटाबेस में कोई मान NULL है, या डेटाबेस में कोई मान है ही नहीं।

एक बेहतर तरीका

रस्ट में nil जैसी कोई चीज़ नहीं होती। इसके बजाय, जब हम यह दर्शाना चाहते हैं कि कोई फंक्शन कभी-कभी एक वैल्यू लौटाता है और कभी-कभी "कुछ नहीं" लौटाता है, तो हम Option का उपयोग करते हैं।

Options एक ऐसा टाइप है जिसमें या तो कोई विशिष्ट मान होता है, या कोई मान नहीं होता। कोड में ये इस तरह दिखते हैं:

Option::Some(42); // संख्या 42 को एक ऑप्शन में लपेटता है
Option::None; // "कोई परिणाम नहीं" दर्शाता है

यह पहले से ही हमारे तदर्थ nil उपयोग से बेहतर दिख रहा है, लेकिन यह और भी बेहतर हो जाता है। रस्ट कंपाइलर आपको None केस पर विचार करने के लिए मजबूर करता है। आप इसे गलती से नज़रअंदाज़ नहीं कर सकते।

match my_option {
  Some(x) => do_something_with_x(x),
  // यदि आप नीचे `None` मैच को हटा देते हैं, तो यह कोड
  // कंपाइल नहीं होगा।
  None => do_the_default_thing()
}

तो हम अपने कलर नेमिंग उदाहरण को रस्ट में इस तरह लिख सकते हैं:

fn color_name(rgb: u32) -> Option<String> {
  if rgb == 0x000000 {
    Some("black".to_owned())
  } else {
    None
  }
}

अब हमें Some और None दोनों कंडीशंस को संभालने के लिए मजबूर किया जाता है:

let name = color_name(0xFFFFFF);
let name = match color_name(0xFFFFFF) {
  Some(value) => value,
  None => "unknown".to_owned(),
}

निश्चित रूप से यह थोड़ा वर्बोज़ और अजीब दिखता है, लेकिन यह उस केस को नज़रअंदाज़ करना असंभव बना देता है जब कोई फंक्शन उपयोगी मान नहीं लौटाता। इसका मतलब है ऐसा कोड जिसे समझना और बनाए रखना आसान हो।

रूबी में Option को लागू करना

रूबी में जो कठोरता की कमी है, उसकी भरपाई वह लचीलेपन से करता है। मैंने सोचा कि रूबी में Option जैसी किसी चीज़ को लागू करने की कोशिश करना दिलचस्प होगा।

हम रूबी में कंपाइल-टाइम एरर्स नहीं बना सकते, क्योंकि यह एक इंटरप्रेटेड भाषा है। लेकिन हम गलत कोड को हमेशा एक अपवाद उठाने का कारण बना सकते हैं, बजाय इसके कि यह केवल एज केस में अपवाद उठाए।

सबसे पहले, आइए दो क्लासेस बनाते हैं। Some एक रीड-ओनली वैल्यू रखता है। None खाली होता है। वे उतने ही सरल हैं जितने लगते हैं।

class Some
  attr_reader :value
  def initialize(value)
    @value = value
  end
end

class None
end

अब, हम अपनी Option क्लास बनाएंगे जो या तो Some या None रखती है और हमें केवल तभी उन्हें एक्सेस करने देती है जब हम दोनों के लिए हैंडलर प्रदान करते हैं।

class Option
  def initialize(value)
    @value = value
  end

  def self.some(value)
    self.new(Some.new(value))
  end

  def self.none()
    self.new(None.new)
  end

  def match(some_lambda, none_lambda)
    if @value.is_a?(Some)
      some_lambda.call(@value.value)
    elsif @value.is_a?(None)
      none_lambda.call()
    else
      raise "Option value must be either Some or None"
    end
  end
end

अंत में, हम अपने कलर उदाहरण को नई Option क्लास का उपयोग करके फिर से लिख सकते हैं:

def color_name(rgb)
  if rgb == 0x000000
    Option.some("black")
  else
    Option.none()
  end
end

puts color_name(0x000000).match(
  -> value { value },
  -> { "no match" }
)
# "black" प्रिंट करता है

निष्कर्ष

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

  1. Ruby में बिटवाइज़ ऑपरेशन में महारत हासिल करें: टिप्स और ट्रिक्स Ruby में बिटवाइज़ ऑपरेशन में महारत हासिल करें: टिप्स और ट्रिक्स

    संभावना है कि आपको आमतौर पर अपनी दैनिक नौकरी में किसी बिटवाइज़ गणित की आवश्यकता नहीं होती। Ruby के बिटवाइज़ AND और OR ऑपरेटर (& और |) शायद उद्देश्य से अधिक दुर्घटना से उपयोग किए जाते हैं। किसने गलती से && के बजाय & टाइप नहीं किया है? लेकिन अगर आप C या असेंबलर जैसी निचले स्तर की भाषाओं में प्रोग्रामि

  1. Ruby में Lambdas में महारत हासिल करें: बुनियादी से उन्नत तकनीकें तक Ruby में Lambdas में महारत हासिल करें: बुनियादी से उन्नत तकनीकें तक

    ब्लॉक Ruby का इतना महत्वपूर्ण हिस्सा हैं, उनके बिना भाषा की कल्पना करना कठिन है। लेकिन लैम्ब्डा? कौन लैम्ब्डा से प्यार करता है? आप वर्षों तक बिना एक का उपयोग किए जा सकते हैं। वे लगभग एक बीते युग के अवशेष लगते हैं। ...लेकिन यह पूरी तरह सच नहीं है। लैम्ब्डा की आस्तीन में कुछ दिलचस्प तरकीबें हैं एक बार

  1. Ruby 2.6 में 9 प्रमुख संवर्द्धन का अन्वेषण Ruby 2.6 में 9 प्रमुख संवर्द्धन का अन्वेषण

    Ruby का एक नया संस्करण नई सुविधाओं और प्रदर्शन सुधारों के साथ आ रहा है। क्या आप परिवर्तनों के साथ बने रहना चाहेंगे? आइए एक नज़र डालते हैं! अंतहीन रेंज (Endless Ranges) Ruby 2.5 और पुराने संस्करण पहले से ही अंतहीन रेंज का एक रूप (Float::INFINITY के साथ) का समर्थन करते हैं, लेकिन Ruby 2.6 इसे अगले स्त