Ruby范围 #
一、范围概述 #
范围(Range)表示一个区间,包含起始值和结束值。范围常用于表示数字区间、字符区间、日期区间等。
ruby
(1..5)
(1...5)
("a".."z")
(Time.now..Time.now + 3600)
二、创建范围 #
2.1 字面量语法 #
ruby
r1 = 1..5
r2 = 1...5
r3 = "a".."z"
r4 = "a"..."z"
r5 = (1..5)
r6 = Range.new(1, 5)
r7 = Range.new(1, 5, true)
2.2 闭区间与半开区间 #
ruby
closed = 1..5
half_open = 1...5
closed.include?(5)
half_open.include?(5)
closed.to_a
half_open.to_a
2.3 无限范围 #
Ruby 2.6+ 支持无限范围:
ruby
(1..)
(1...)
(..10)
(...10)
(-Float::INFINITY..Float::INFINITY)
三、范围方法 #
3.1 基本属性 #
ruby
r = 1..5
r.begin
r.end
r.first
r.last
r.min
r.max
r.size
r.length
r.count
3.2 包含检查 #
ruby
r = 1..5
r.include?(3)
r.include?(5)
r.include?(6)
r.member?(3)
r === 3
r.cover?(3)
r.cover?(6)
3.3 include? vs cover? #
ruby
r = "a".."z"
r.include?("aa")
r.cover?("aa")
r = 1..5
r.cover?(3.5)
r.include?(3.5)
3.4 边界检查 #
ruby
r = 1..5
r.begin
r.end
r.first
r.last
r.first(3)
r.last(3)
r.minmax
四、范围迭代 #
4.1 each迭代 #
ruby
(1..5).each { |n| puts n }
("a".."e").each { |c| puts c }
(1..5).each_with_index { |n, i| puts "#{i}: #{n}" }
4.2 step步进 #
ruby
(1..10).step(2) { |n| puts n }
(1..10).step(2).to_a
(0..100).step(10).to_a
("a".."z").step(5).to_a
4.3 转换为数组 #
ruby
(1..5).to_a
(1..5).entries
(1..5).to_a
("a".."e").to_a
4.4 无限范围迭代 #
ruby
(1..).take(5)
(1..).first(5)
(1..).lazy.take(5).to_a
(1..).step(2).take(5).to_a
五、范围比较 #
5.1 比较运算 #
ruby
(1..5) == (1..5)
(1..5) == (1...6)
(1..5) === 3
(1..5) === 6
(1..5) === 3.5
5.2 重叠检查 #
ruby
def overlaps?(r1, r2)
r1.cover?(r2.begin) || r2.cover?(r1.begin)
end
overlaps?((1..5), (3..7))
overlaps?((1..5), (6..10))
5.3 范围包含 #
ruby
def contains?(outer, inner)
outer.begin <= inner.begin && outer.end >= inner.end
end
contains?((1..10), (3..7))
contains?((1..10), (5..15))
六、范围应用 #
6.1 case语句 #
ruby
def grade(score)
case score
when 90..100 then "A"
when 80...90 then "B"
when 70...80 then "C"
when 60...70 then "D"
else "F"
end
end
grade(95)
grade(85)
grade(55)
6.2 日期范围 #
ruby
require 'date'
start_date = Date.new(2024, 1, 1)
end_date = Date.new(2024, 1, 7)
(start_date..end_date).each { |date| puts date }
(start_date..end_date).select(&:weekday?)
(start_date..end_date).count { |d| d.saturday? || d.sunday? }
6.3 时间范围 #
ruby
require 'time'
start_time = Time.now
end_time = start_time + 3600
(start_time..end_time).cover?(Time.now + 1800)
(start_time..end_time).cover?(Time.now + 7200)
6.4 字符范围 #
ruby
("a".."z").to_a
("A".."Z").to_a
("0".."9").to_a
("a".."z").include?("m")
("a".."z").cover?("aa")
6.5 数组切片 #
ruby
arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr[1..5]
arr[1...5]
arr[2..-1]
arr[2..]
arr[..5]
arr[...-2]
七、范围与枚举 #
7.1 枚举方法 #
ruby
(1..10).map { |n| n * 2 }
(1..10).select { |n| n.even? }
(1..10).reduce(:+)
(1..10).sum
(1..10).minmax
(1..10).any? { |n| n > 5 }
(1..10).all? { |n| n > 0 }
7.2 懒加载 #
ruby
(1..).lazy
.select { |n| n.even? }
.map { |n| n * 2 }
.take(5)
.to_a
(1..).lazy.take(3).force
(1..).lazy.select(&:even?).first(5)
7.3 无限序列 #
ruby
natural_numbers = (1..)
natural_numbers.take(10).to_a
fibonacci = Enumerator.new do |yielder|
a, b = 0, 1
loop do
yielder << a
a, b = b, a + b
end
end
fibonacci.take(10).to_a
八、范围操作 #
8.1 范围合并 #
ruby
def merge_ranges(r1, r2)
return nil unless overlaps?(r1, r2)
Range.new([r1.begin, r2.begin].min, [r1.end, r2.end].max)
end
merge_ranges((1..5), (3..7))
merge_ranges((1..5), (6..10))
8.2 范围差集 #
ruby
def range_difference(r1, r2)
return [r1] unless overlaps?(r1, r2)
result = []
result << (r1.begin...r2.begin) if r1.begin < r2.begin
result << (r2.end + 1..r1.end) if r1.end > r2.end
result
end
range_difference((1..10), (3..7))
range_difference((1..10), (1..5))
8.3 范围分割 #
ruby
def split_range(range, step)
range.step(step).each_cons(2).map { |a, b| a...b }
end
split_range((1..10), 3)
九、自定义范围 #
9.1 可比较对象 #
ruby
class Point
include Comparable
attr_reader :x, :y
def initialize(x, y)
@x, @y = x, y
end
def <=>(other)
[x, y] <=> [other.x, other.y]
end
def succ
if y < 10
Point.new(x, y + 1)
else
Point.new(x + 1, 0)
end
end
end
p1 = Point.new(0, 0)
p2 = Point.new(1, 1)
(p1..p2).to_a
9.2 时间范围扩展 #
ruby
require 'time'
class TimeRange < Range
def duration
(self.end - self.begin).to_i
end
def hours
duration / 3600
end
def minutes
duration / 60
end
def each_hour
return enum_for(:each_hour) unless block_given?
current = self.begin
while current <= self.end
yield current
current += 3600
end
end
end
range = TimeRange.new(Time.now, Time.now + 7200)
range.duration
range.hours
range.each_hour.to_a
十、实用示例 #
10.1 分页 #
ruby
def paginate(total, page, per_page)
start = (page - 1) * per_page
finish = [start + per_page - 1, total - 1].min
start..finish
end
paginate(100, 1, 10)
paginate(100, 2, 10)
paginate(100, 10, 10)
paginate(100, 11, 10)
10.2 时间槽 #
ruby
def time_slots(start_time, end_time, duration_minutes)
duration = duration_minutes * 60
(start_time.to_i..end_time.to_i).step(duration).map do |t|
Time.at(t)
end
end
time_slots(Time.now, Time.now + 7200, 30)
10.3 IP地址范围 #
ruby
def ip_range(start_ip, end_ip)
start = start_ip.split(".").map(&:to_i)
finish = end_ip.split(".").map(&:to_i)
(start.join(".").to_i(256)..finish.join(".").to_i(256)).map do |n|
[
(n >> 24) & 255,
(n >> 16) & 255,
(n >> 8) & 255,
n & 255
].join(".")
end
end
ip_range("192.168.1.1", "192.168.1.5")
10.4 版本号比较 #
ruby
class Version
include Comparable
attr_reader :parts
def initialize(version)
@parts = version.split(".").map(&:to_i)
end
def <=>(other)
parts <=> other.parts
end
def to_s
parts.join(".")
end
end
v1 = Version.new("1.0.0")
v2 = Version.new("2.0.0")
(v1..v2).cover?(Version.new("1.5.0"))
十一、总结 #
本章我们学习了:
- 创建范围:…、…、Range.new
- 范围方法:begin、end、include?、cover?
- 范围迭代:each、step、to_a
- 无限范围:(1…)、(…10)
- 范围应用:case语句、日期范围、数组切片
- 自定义范围:实现<=>和succ方法
接下来让我们学习Ruby的集合!
最后更新:2026-03-27