1. 什么是范围?

范围(Range)表示一个连续的区间,由起始值和结束值定义。Ruby 中用 ..... 创建范围:

  • ..:包含结束值(闭区间)。
  • ...:不包含结束值(半开区间)。

基本定义

range1 = 1..5    # 包含 1 到 5
range2 = 1...5   # 包含 1 到 4,不含 5
puts range1.to_a # [1, 2, 3, 4, 5]
puts range2.to_a # [1, 2, 3, 4]

2. 创建范围

数字范围

numbers = 0..3
puts numbers.to_a  # [0, 1, 2, 3]

字符范围

letters = "a".."c"
puts letters.to_a  # ["a", "b", "c"]

# 中文字符
chars = "你".."他"
puts chars.to_a    # ["你", "他"](按 Unicode 顺序)

注意

  • 字符范围按 Unicode 编码排序,中文支持有限(通常只包含起止点)。

3. 常用方法

转换为数组

range = 1..4
puts range.to_a  # [1, 2, 3, 4]

检查包含

  • include?cover?
  range = 1..5
  puts range.include?(3)   # true
  puts range.include?(6)   # false
  puts range.cover?(2.5)   # true(在范围内)
  • include?:检查是否为离散成员。
  • cover?:检查是否在连续范围内(对浮点数更友好)。

边界值

range = 2..7
puts range.begin   # 2(起始值)
puts range.end     # 7(结束值)
puts range.first   # 2
puts range.last    # 7

大小

puts (1..5).size   # 5
puts (1...5).size  # 4

4. 范围与循环

each

(1..3).each do |n|
  puts "第 #{n} 次"
end

输出:

第 1 次
第 2 次
第 3 次

for

for i in 1..3
  puts "计数:#{i}"
end

输出:

计数:1
计数:2
计数:3

times(结合范围)

(0..2).each { |i| puts "你好 #{i}" }

输出:

你好 0
你好 1
你好 2

5. 范围与条件判断

case

score = 85
result = case score
         when 90..100
           "优秀"
         when 80..89
           "良好"
         else
           "继续努力"
         end
puts result  # 良好

include?

age = 25
if (18..30).include?(age)
  puts "你是年轻人"
end

输出:

你是年轻人

6. 范围与切片

数组切片

fruits = ["苹果", "香蕉", "橙子", "葡萄"]
puts fruits[1..2]   # ["香蕉", "橙子"]
puts fruits[0...-1] # ["苹果", "香蕉", "橙子"]

字符串切片

text = "你好,世界"
puts text[0..1]   # 你好
puts text[2..-1]  # ,世界

7. 中文支持示例

数字范围

任务 = ["任务1", "任务2", "任务3", "任务4"]
(0..2).each do |i|
  puts "执行 #{任务[i]}"
end

输出:

执行 任务1
执行 任务2
执行 任务3

实践案例

class TaskScheduler
  def initialize(tasks)
    @tasks = tasks
  end

  def run(range)
    range.each do |i|
      puts "执行 #{@tasks[i]}"
    end
  end
end

scheduler = TaskScheduler.new(["写代码", "开会", "休息"])
scheduler.run(0..1)

输出:

执行 写代码
执行 开会

8. 实践案例

案例 1:打印日期范围

require 'date'
start = Date.new(2025, 3, 22)
range = start..start + 2
range.each { |d| puts d.strftime("%Y年%m月%d日") }

输出:

2025年03月22日
2025年03月23日
2025年03月24日

案例 2:数字筛选

numbers = (1..10).to_a
even = numbers.select { |n| (2..8).include?(n) && n % 2 == 0 }
puts even  # [2, 4, 6, 8]

案例 3:简单游戏

target = rand(1..10)
guess_range = 1..10
guess_range.each do |guess|
  if guess == target
    puts "猜中了!目标是 #{target}"
    break
  else
    puts "猜 #{guess},不对"
  end
end

输出(示例):

猜 1,不对
猜 2,不对
猜 3,猜中了!目标是 3

9. 注意事项

  • .. vs ...:注意包含与否:
  puts (1..3).to_a   # [1, 2, 3]
  puts (1...3).to_a  # [1, 2]
  • 性能:范围是惰性对象,调用 to_aeach 时才生成完整序列。
  • 类型一致性:范围的起止值应为可比较对象(如数字、字符串)。

10. 高级用法

自定义对象范围

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

  def <=>(other)
    @value <=> other.value
  end
end

v1 = Version.new("1.0")
v2 = Version.new("2.0")
range = v1..v2
puts range.cover?(Version.new("1.5"))  # true

无穷范围(Ruby 2.6+)

range = 1..
range.each do |n|
  puts n
  break if n > 3
end

输出:

1
2
3
4

下一步

  • 练习:告诉我你想用范围做什么(循环、切片等),我可以设计一个例子。
  • 问题解答:对范围用法有疑问吗?直接问我!
  • 深入学习:想了解范围与迭代器或正则表达式的结合吗?我可以继续讲解。

你现在想做什么?写代码、问问题,还是其他?