Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

การจัดการศูนย์แบบชนบทใน Ruby

ในช่วงหกเดือนที่ผ่านมาฉันได้ใช้โปรแกรมจำลอง NES ใน Rust อย่างที่คุณคาดไว้ ฉันได้เรียนรู้มากมายเกี่ยวกับการเกิดสนิม และเพิ่มเติมเกี่ยวกับ NES ภายใน แต่ประสบการณ์ก็เปลี่ยนวิธีที่ฉันมอง Ruby ด้วย

โดยเฉพาะอย่างยิ่ง มันทำให้ฉันหวาดระแวงเล็กน้อยเกี่ยวกับวิธีการที่คืนค่า ไม่มี .

ถ้าคุณไม่ยืนหยัดเพื่อบางสิ่ง คุณจะตกหลุมรักทุกสิ่ง

ไม่มีอะไร ไม่มี หมายถึงในทับทิม? เกือบทุกอย่าง เมื่อเมธอดคืนค่าเป็นศูนย์ อาจหมายถึง:

  • เมธอดนี้ไม่มีค่าส่งคืน
  • ปกติจะมีค่าส่งคืนแต่ไม่ใช่ครั้งนี้
  • ส่งคืนค่าจากฐานข้อมูลซึ่งเป็น NULL
  • มีเรื่องไม่คาดฝันเกิดขึ้น

ทำให้โค้ดอ่านยากและเป็นสาเหตุหลักของ ที่พบบ่อยที่สุด ข้อยกเว้น Ruby ใน Ruby:NoMethodError . ในฐานะเจ้าของส่วนหนึ่งของบริการตรวจสอบข้อยกเว้น NoMethodError กำลังส่งลูกเข้าโรงเรียน

ดูรหัสต่อไปนี้ คืนค่า ไม่มี ส่วนใหญ่เพราะ ถ้า คำสั่งประเมินเป็น ไม่มี เมื่อเงื่อนไขไม่ตรงกันและไม่มี อื่น .

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

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

หากคุณเป็นนักพัฒนา Ruby มากประสบการณ์ คุณจะรู้ว่ารูกระต่ายนี้ลึกกว่านั้นมาก บางครั้งความหมายที่แตกต่างกันเหล่านี้ของ nil ทับซ้อนกันในลักษณะแปลก ๆ ทำให้ไม่สามารถทราบได้ ตัวอย่างเช่น ค่าในฐานข้อมูลคือ NULL หรือไม่มีค่าในฐานข้อมูลเลย

ทางที่ดีกว่า

ใน Rust ไม่มีสิ่งที่เรียกว่า ไม่มี . เมื่อเราต้องการแสดงว่าบางครั้งฟังก์ชันคืนค่าและบางครั้งคืนค่า "ไม่มี" เราใช้ Option .

ตัวเลือก เป็นประเภทที่มีค่าเฉพาะหรือไม่มีค่า นี่คือหน้าตาของโค้ด:

Option::Some(42); // Wraps the number 42 in an option
Option::None;     // Indicates "no result"

นี่ดูดีกว่าโฆษณาเฉพาะของเรา ไม่มี การใช้งานแต่จะดียิ่งขึ้นไปอีก คอมไพเลอร์ Rust บังคับให้คุณพิจารณา ไม่มี กรณี. คุณไม่สามารถละเลยได้โดยบังเอิญ

match my_option {
  Some(x) => do_something_with_x(x),
  // If you remove the `None` match below, this code
  // won't compile.
  None => do_the_default_thing()  
}

ดังนั้นเราสามารถเขียนตัวอย่างการตั้งชื่อสีของเราเป็นสนิมได้ดังนี้:

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

ตอนนี้เราถูกบังคับให้จัดการทั้ง Some และ ไม่มี เงื่อนไข:

let name = color_name(0xFFFFFF);

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

แน่นอนว่านี่ดูละเอียดและแปลกไปเล็กน้อย แต่ก็ทำให้ไม่สามารถเพิกเฉยต่อกรณีนี้เมื่อฟังก์ชันไม่คืนค่าที่เป็นประโยชน์ นั่นหมายถึงโค้ดที่เข้าใจและบำรุงรักษาได้ง่ายขึ้น

การนำตัวเลือกไปใช้ใน Ruby

สิ่งที่ Ruby ขาดความเข้มงวดนั้นชดเชยด้วยความยืดหยุ่น ฉันคิดว่ามันน่าสนใจที่จะลองใช้บางอย่างเช่น Option ในทับทิม

เราไม่สามารถสร้างข้อผิดพลาดเวลาคอมไพล์ใน Ruby ได้ เนื่องจากเป็นภาษาที่ตีความ แต่เราสามารถทำให้รหัสไม่ถูกต้อง เสมอ ยกข้อยกเว้น แทนที่จะเพิ่มข้อยกเว้นเฉพาะเมื่อคุณพบกรณีขอบ

ขั้นแรก ให้สร้างสองคลาส บางส่วน มีค่าอ่านอย่างเดียว ไม่มี มันว่างเปล่า. เรียบง่ายอย่างที่คิด

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

  class None
  end

ต่อไป เราจะสร้าง Option คลาสที่มี บางส่วน หรือ ไม่มี และอนุญาตให้เราเข้าถึงได้ก็ต่อเมื่อเราให้ตัวจัดการสำหรับทั้งคู่

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" })

# Prints "black"

บทสรุป

ฉันยังไม่ได้ลองใช้เทคนิคนี้ในโครงการจริง ฉันคิดว่ามันป้องกัน NoMethodErrors . ได้มากมายอย่างแน่นอน ที่ลื่นไหลในการผลิตอยู่เสมอ มันดูค่อนข้างยุ่งยากและไม่ใช่ Rubyish มากนัก แต่ฉันคิดว่าด้วยการปรับแต่งบางอย่าง ไวยากรณ์ที่น่าพึงพอใจจะปรากฏขึ้น