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

4 รูปแบบการท่องจำอย่างง่ายใน Ruby (และหนึ่งอัญมณี)

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

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

การช่วยจำขั้นพื้นฐานขั้นสูง

คุณจะเห็นรูปแบบการท่องจำนี้ตลอดเวลาใน Ruby:

app/models/order.rb
class User < ActiveRecord::Base
  def twitter_followers
    # assuming twitter_user.followers makes a network call
    @twitter_followers ||= twitter_user.followers
  end
end

||= มากหรือน้อยแปลเป็น @twitter_followers = @twitter_followers || twitter_user.followers . หมายความว่าคุณจะโทรผ่านเครือข่ายในครั้งแรกที่คุณโทร twitter_followers และการเรียกในอนาคตจะคืนค่าของตัวแปรอินสแตนซ์ @twitter_followers .

การท่องจำหลายบรรทัด

บางครั้ง โค้ดที่ช้าจะไม่พอดีกับบรรทัดเดียวโดยไม่ได้ทำอะไรแย่ๆ กับมัน มีสองสามวิธีในการขยายรูปแบบพื้นฐานเพื่อทำงานกับโค้ดหลายบรรทัด แต่วิธีนี้ชอบที่สุด:

app/models/order.rb
class User < ActiveRecord::Base
  def main_address
    @main_address ||= begin
      maybe_main_address = home_address if prefers_home_address?
      maybe_main_address = work_address unless maybe_main_address
      maybe_main_address = addresses.first unless maybe_main_address
    end
  end
end

begin...end สร้างบล็อกของรหัสใน Ruby ที่สามารถถือเป็นสิ่งเดียว เช่น {...} ในภาษาสไตล์ C นั่นเป็นเหตุผลที่ ||= ทำงานได้ดีเหมือนเมื่อก่อน

แล้วไม่มีเลย

แต่รูปแบบการท่องจำเหล่านี้มีปัญหาที่น่ารังเกียจและซ่อนเร้น ในตัวอย่างแรก จะเกิดอะไรขึ้นถ้าผู้ใช้ไม่มีบัญชี Twitter และ API ผู้ติดตาม Twitter ส่งคืน nil ? ในวินาทีที่ 2 จะเกิดอะไรขึ้นถ้าผู้ใช้ไม่มีที่อยู่และบล็อกส่งคืน nil ?

ทุกครั้งที่เราเรียกใช้เมธอด ตัวแปรอินสแตนซ์จะเป็น nil และเราจะทำการดึงข้อมูลราคาแพงอีกครั้ง

ดังนั้น ||= คงไม่ใช่ทางที่ถูกต้อง แต่เราต้องแยกความแตกต่างระหว่าง nil และ undefined :

app/models/order.rb
class User < ActiveRecord::Base
  def twitter_followers
    return @twitter_followers if defined? @twitter_followers
    @twitter_followers = twitter_user.followers
  end
end
app/models/order.rb
class User < ActiveRecord::Base
  def main_address
    return @main_address if defined? @main_address
    @main_address = begin
      main_address = home_address if prefers_home_address?
      main_address ||= work_address
      main_address ||= addresses.first # some semi-sensible default
    end
  end
end

น่าเสียดาย มันดูน่าเกลียดกว่าเล็กน้อย แต่ใช้งานได้กับ nil , false และทุกสิ่งทุกอย่าง (เพื่อจัดการกับ nil กรณี คุณสามารถใช้ Null Objects และอาร์เรย์ว่างเพื่อหลีกเลี่ยงปัญหานี้ได้ อีกเหตุผลหนึ่งที่ควรหลีกเลี่ยง nil !)

แล้วพารามิเตอร์ล่ะ

เรามีรูปแบบการท่องจำที่ใช้งานได้ดีกับอุปกรณ์เสริมทั่วไป แต่ถ้าคุณต้องการบันทึกเมธอดที่ใช้พารามิเตอร์แบบนี้ล่ะ

app/models/city.rb
class City < ActiveRecord::Base
  def self.top_cities(order_by)
    where(top_city: true).order(order_by).to_a
  end
end

ปรากฎว่า Hash . ของ Ruby มีตัวย่อที่ทำงาน สมบูรณ์แบบ สำหรับสถานการณ์นี้ คุณสามารถโทร Hash.new ด้วยบล็อก:

Hash.new {|h, key| h[key] = some_calculated_value }

จากนั้น ทุกครั้งที่คุณพยายามเข้าถึงคีย์ในแฮชที่ยังไม่ได้ตั้งค่า ระบบจะดำเนินการบล็อก และจะส่งแฮชเองพร้อมกับคีย์ที่คุณพยายามเข้าถึงในบล็อก

ดังนั้น หากคุณต้องการจำวิธีนี้ คุณสามารถทำสิ่งต่อไปนี้:

app/models/city.rb
class City < ActiveRecord::Base
  def self.top_cities(order_by)
    @top_cities ||= Hash.new do |h, key|
      h[key] = where(top_city: true).order(key).to_a
    end
    @top_cities[order_by]
  end
end

และไม่ว่าคุณจะส่งอะไรเข้าไปใน order_by ผลลัพธ์ที่ถูกต้องจะได้รับการบันทึก เนื่องจากบล็อกจะถูกเรียกเมื่อไม่มีคีย์เท่านั้น คุณจึงไม่ต้องกังวลว่าผลลัพธ์ของการบล็อกจะเป็นศูนย์หรือเป็นเท็จ

น่าแปลกที่ Hash ใช้งานได้ดีกับคีย์ที่เป็นอาร์เรย์จริงๆ:

h = {}
h[["a", "b"]] = "c"
h[["a", "b"]] # => "c"

ดังนั้นคุณสามารถใช้รูปแบบนี้ในวิธีการที่มีพารามิเตอร์จำนวนเท่าใดก็ได้!

เหตุใดจึงต้องประสบปัญหาทั้งหมดนี้

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

ดังนั้น หากคุณกำลังทำงานกับแอปที่ต้องการการจดบันทึกจำนวนมาก คุณอาจต้องการใช้อัญมณีที่จัดการการท่องจำให้คุณด้วย API ที่ดีและเป็นมิตร Memoist ดูเหมือนจะเป็นสิ่งที่ดีและค่อนข้างคล้ายกับที่ Rails เคยมี (หรือด้วยความรู้เรื่องการจดบันทึกที่คุณเพิ่งค้นพบ คุณอาจลองสร้างมันขึ้นมาเองก็ได้)

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