วิธีหนึ่งในการเรียนรู้เทคนิค รูปแบบ และเมธอด Ruby ใหม่ๆ คือการอ่านโค้ดที่คนอื่นเขียน
แต่คุณหา โค้ดที่น่าสนใจและ值得อ่าน ได้ที่ไหน?
เรามีโปรเจกต์โอเพนซอร์สเยอะ และเว็บไซต์การแข่งขันเขียนโค้ด ที่ให้คุณดูโซลูชันจากนักพัฒนาคนอื่นได้เมื่อคุณส่งของตัวเองแล้ว
ในบทความนี้:
ผมรวบรวม 7 ตัวอย่างที่ผมคิดว่าคุณจะชอบ
ผมจะให้ความคิดเห็นบนแต่ละตัวอย่างด้วย เพื่อช่วยให้คุณได้ประโยชน์สูงสุด
ผลรวมของสองตัวเลข
ในตัวอย่างนี้ เราต้องการทราบว่า ถ้ากำหนดอาร์เรย์ของตัวเลขที่ไม่ซ้ำกัน มีการรวมกันของสองตัวเลขที่ผลรวมเท่ากับตัวเลขเป้าหมายหรือไม่
โค้ด:
def sum_eq_n?(arr, n)
return true if arr.empty? && n == 0
arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n }
end
นี่น่าสนใจเพราะผมใช้เมธอด product ที่นี่
เมื่อคุณใช้เมธอดนี้ มันเหมือนมีลูปซ้อนลูปที่รวมค่าทั้งหมดในอาร์เรย์ A กับค่าทั้งหมดในอาร์เรย์ B
นับ Mapping และการค้นหา
สมมติว่าคุณต้องการหาตัวเลขที่หายไปในลำดับเลขคณิต เช่น (2,4,6,10)
เราสามารถใช้กลยุทธ์ที่คำนวณความต่างระหว่างตัวเลข
[2, 2, 4]
เป้าหมายที่นี่คือการค้นหาว่าลำดับคืออะไร
มันเพิ่มขึ้นหรือลดลง?
เพิ่มขึ้นเท่าไหร่?
โค้ดนี้เผยลำดับ:
differences = [2, 2, 4]
differences.max_by { |n| differences.count(n) } # 2
# นี่คือการเพิ่มขึ้นระหว่างตัวเลขในลำดับ
เมื่อเรารู้ลำดับแล้ว เราสามารถเปรียบเทียบตัวเลขทั้งหมดเพื่อหาตัวที่หายไป
โค้ดที่นี่:
def find_missing(sequence)
consecutive = sequence.each_cons(2)
differences = consecutive.map { |a,b| b - a }
sequence = differences.max_by { |n| differences.count(n) }
missing_between = consecutive.find { |a,b| (b - a) != sequence }
missing_between.first + sequence
end
find_missing([2,4,6,10]) # 8
นับดูว่าเราใช้เมธอด Ruby กี่ตัวที่ทำงานหนักให้เรา 🙂
ตัวอย่าง Regular Expression
ถ้าคุณทำงานกับสตริงและต้องการหาแพทเทิร์น Regular expressions คือเพื่อนของคุณ
มันอาจลำบากหน่อยที่จะทำให้ถูกต้อง แต่วิธีฝึกทำให้เก่ง!
ตอนนี้:
สมมติว่าเราต้องการทราบว่าสตริงที่กำหนดปฏิบัติตามแพทเทิร์นของ ตัวอักษรสระ (VOWEL) ถึง ตัวอักษรไม่ใช่สระ (NON-VOWEL) หรือไม่
แบบนี้:
"ateciyu"
เราสามารถใช้ regular expression ร่วมกับเมธอด match? เพื่อค้นหาได้
ตัวอย่างโค้ด:
def alternating_characters?(s)
type = [/[aeiou]/, /[^aeiou]/].cycle
if s.start_with?(/[^aeiou]/)
type.next
end
s.chars.all? { |ch| ch.match?(type.next) }
end
alternating_characters?("ateciyu") # true
สังเกตบางอย่าง:
- เราใช้เมธอด
cycleเพื่อสลับไปมาระหว่าง regex สระ และ regex ไม่ใช่สระ - เราแปลงสตริงเป็นอาร์เรย์ตัวอักษรด้วย
charsเพื่อใช้เมธอดall?
ตัวอย่าง Recursion และ Stack
Recursion (การเรียกซ้ำ) คือเมื่อเมธอดเรียกตัวเองหลายครั้งเป็นวิธีก้าวหน้าสู่คำตอบ
ปัญหาที่น่าสนใจหลายอย่างสามารถ แก้ได้ด้วย recursion
แตเพราะ recursion มีขีดจำกัด คุณสามารถใช้โครงสร้างข้อมูล stack แทนได้
ตอนนี้:
มาดูตัวอย่างที่เราต้องการหา "Power Set" ของอาร์เรย์ที่กำหนด Power Set คือเซตของทุก subset ที่สามารถสร้างจากอาร์เรย์ได้
ตัวอย่างด้วย recursion:
def get_numbers(list, index = 0, taken = []) return [taken] if index == list.size get_numbers(list, index+1, taken) + get_numbers(list, index+1, taken + [list[index]]) end get_numbers([1,2,3])
ปัญหาเดียวกันแก้ด้วย stack:
def get_numbers_stack(list)
stack = [[0, []]]
output = []
until stack.empty?
index, taken = stack.pop
next output << taken if index == list.size
stack.unshift [index + 1, taken]
stack.unshift [index + 1, taken + [list[index]]]
end
output
end
ไอเดียคือ ในแต่ละรอบของอัลกอริทึม เรา either เลือกตัวเลข หรือไม่เลือก
เราแยกกิ่ง และลองทั้งสองผลลัพธ์ เพื่อ สร้างทุกการรวมเป็นไปได้
นึกภาพต้นไม้ที่ใบแต่ละใบคือหนึ่งในคำตอบ
สังเกตบางอย่าง:
- คำตอบ recursion สั้นกว่า
- ส่วน "การก้าวหน้า" จริงของอัลกอริทึม (index + 1) เกือบเหมือนกัน
- Stack ที่เราใช้คือแค่อาร์เรย์ เพราะ Ruby ไม่มีคลาส
Stack
ตัวอย่าง Method Chaining
นี่คือตัวอย่างโปรดของผม เพราะมันแสดงให้เห็นว่า Ruby อันทรงพลังแค่ไหน
การรวมเมธอดช่วยให้คุณ นำเอาต์พุตที่ผลิตโดยเมธอดหนึ่งไปต่อให้อีกเมธอด
เหมือนสายการผลิตโรงงาน!
คุณเริ่มต้นด้วยวัตถุดิบ (input) แล้วผ่านกระบวนการเรียกเมธอดเหล่านี้ คุณเปลี่ยนวัตถุดิบให้กลายเป็นผลลัพธ์ที่ต้องการอย่างช้าๆ
ตัวอย่าง:
def longest_repetition(string)
max = string
.chars
.chunk(&:itself)
.map(&:last)
.max_by(&:size)
max ? [max[0], max.size] : ["", 0]
end
longest_repetition("aaabb") # ["a", 3]
เมื่อได้รับสตริง โค้ดนี้จะหา ตัวอักษรที่ซ้ำต่อเนื่องกันยาวที่สุด
หมายเหตุ:
- โค้ดจัดรูปแบบเพื่อเพิ่มความอ่านง่ายสูงสุด
- การใช้รูปแบบ
Symbol#to_proc(&:size)
ก็อีกอย่าง อย่าสับสนกับ "Law of Demeter"
"กฎ" นั้นเกี่ยวกับการเข้าถึงภายในของออบเจ็กต์อื่น
ที่นี่เราแค่แปลงออบเจ็กต์
ตัวอย่าง With Index
คุณอยากได้ดัชนีปัจจุบันขณะวนลูปคอลเลกชันไหม?
คุณสามารถใช้เมธอด with_index ได้
ตัวอย่าง:
def reverse_alternate(string)
string.gsub(/[^\s]+/).with_index { |w, idx| idx.even? ? w : w.reverse }
end
reverse_alternate("Apples Are Good") # "Apples erA Good"
สังเกต:
- เรารวม
with_indexและeven?เพื่อหาว่าต้องกลับคำปัจจุบันหรือไม่ - Gsub โดยไม่มีบล็อกคืนค่าออบเจ็กต์
Enumeratorซึ่งช่วยให้คุณเชน (chain) กับเมธอดอื่นได้
ตัวอย่าง Each With Object
เมธอดน่าสนใจอีกตัวคือ each_with_object และเพื่อนของมัน with_object
คุณสามารถใช้สองเมธอดนี้เมื่อคุณต้องการออบเจ็กต์เพื่อเก็บผลลัพธ์
ตัวอย่าง:
def clean_string(str)
str
.chars
.each_with_object([]) { |ch, obj| ch == "#" ? obj.pop : obj << ch }
.join
end
clean_string("aaa#b")
ในตัวอย่างนี้ เราต้องการลบตัวอักษรสุดท้ายเมื่อเจอสัญลักษณ์ #
สังเกต:
each_with_objectรับอาร์กิวเมนต์ ซึ่งคือออบเจ็กต์ที่เราต้องการเริ่มต้น อาร์กิวเมนต์นี้กลายเป็นพารามิเตอร์บล็อกตัวที่ 2- เราแปลงสตริงเป็นอาร์เรย์ตัวอักษร (
chars) และกลับเป็นสตริงเมื่อเสร็จ (join) - เราใช้ ternary operator เพื่อตัดสินใจ ทำให้โค้ดสั้นลง
สรุป
ในบทความนี้ คุณได้เห็นตัวอย่างโค้ดน่าสนใจบางอย่างพร้อมความคิดเห็นและคำอธิบายของผม เพื่อช่วยให้คุณเขียนโค้ด Ruby ได้ดีขึ้น
อย่าลืมแชร์บทความนี้เพื่อให้คนอื่นเพลิดเพลินไปกับมัน
ขอบคุณที่อ่านครับ!