Range trong Ruby là gì và nó hoạt động như thế nào?
Range là đối tượng có giá trị bắt đầu và kết thúc, giúp tạo chuỗi trải dài toàn bộ khoảng giữa hai giá trị này.
Bạn có thể đi từ 1 đến 20, hoặc từ "a" đến "z".
Trong bài này bạn sẽ khám phá:
- Cách tạo Range trong Ruby
- Cách dùng phương thức
stepvàinclude? - Cách Range hoạt động bên trong
Bắt đầu thôi!
Hiểu Về Range
Nhắc lại, cú pháp Range Ruby trông như sau:
(1..20)
Dấu ngoặc đơn không bắt buộc để định nghĩa Range.
Nhưng nếu muốn gọi phương thức trên range thì cần chúng. Nếu không, bạn đang gọi phương thức trên phần tử thứ hai của range, thay vì trên range đó.
Lớp Range include Enumerable, nên bạn có tất cả phương thức lặp mạnh mẽ mà không cần chuyển range thành mảng.
Phương Thức Step Trong Ruby
Range có phương thức hữu ích như step.
Ví dụ:
(10..20).step(2).to_a # [10, 12, 14, 16, 18, 20]
Dùng phương thức này để lặp qua range với bước nhảy n, nơi n là đối số truyền cho step.
Kiểm Tra Số Có Nằm Trong Range
Các phương thức Range khác cần biết: cover? và include?.
Sẽ là sai lầm nếu nghĩ chúng làm cùng việc, vì thực tế không.
Phương thức include? làm như mong đợi: kiểm tra sự bao gồm bên trong range. Tương đương mở rộng Range thành Array và kiểm tra xem phần tử có trong đó không.
Nhưng cover? khác biệt, nó chỉ kiểm tra đối với giá trị đầu và cuối của range (begin <= obj <= end), có thể cho kết quả bất ngờ.
Ví dụ:
('a'..'z').include? "cc" # false
('a'..'z').cover? "cc" # true
Ví dụ cover? tương đương:
"a" <= "cc" && "cc" <= "z"
Lý do trả về true là vì chuỗi được so sánh ký tự theo ký tự. Vì "a" đứng trước "c", các ký tự sau "c" đầu tiên không quan trọng.
Range Được Triển Khai Như Thế Nào Trong Ruby
Range không giới hạn ở số và chữ, bạn có thể dùng bất kỳ đối tượng nào miễn là chúng triển khai các phương thức: <=> và succ.
Ví dụ, Range DateTime:
require 'time'
t1 = DateTime.new
t2 = DateTime.new + 30
next_30_days = t1..t2
# Ví dụ sử dụng
next_30_days.select(&:friday?).map(&:day)
Vậy cơ chế hoạt động ra sao? Xem triển khai sau:
def range(a, b)
# nếu phần tử đầu lớn hơn phần tử hai
# thì đây không phải range tuần tự
return [] if a > b
out = []
# tiến tới khi phần tử hai giống phần tử một
while a != b
out << a
a = a.next
end
# thêm phần tử cuối (range bao gồm cuối)
# cũng trả về kết quả qua implicit return
out << a
end
p range 1, 10
p range 'a', 'z'
Đã thêm chú thích để bạn hiểu rõ đang xảy ra gì.
Ý tưởng là liên tục gọi phương thức next trên đối tượng đầu tiên cho đến khi nó bằng đối tượng thứ hai, giả định chúng cuối cùng sẽ gặp nhau.
Range Với Lớp Tùy Chỉnh
Phần lớn thời gian bạn dùng range số và ký tự, nhưng vẫn tốt khi biết cách dùng range trong lớp tùy chỉnh.
Ví dụ:
class LetterMultiplier
attr_reader :count
include Comparable
def initialize(letter, count)
@letter = letter
@count = count
end
def succ
self.class.new(@letter, @count + 1)
end
def <=>(other)
count <=> other.count
end
end
a = LetterMultiplier.new('w', 2)
b = LetterMultiplier.new('w', 8)
# In mảng với tất cả phần tử trong range
p Array(a..b)
Chìa khóa là triển khai đúng phương thức <=> và succ.
Nếu muốn dùng phương thức include? cần include module Comparable, thêm các phương thức như ==, <, > (dựa trên kết quả <=>).
Kết Luận
Bài viết này bạn đã học Range hoạt động như thế nào trong Ruby để hiểu sâu hơn và triển khai đối tượng riêng hỗ trợ thao tác range.
Đừng quên đăng ký bản tin bên dưới để không bỏ lỡ bài tiếp theo 🙂