Skip to content

Ruby 范围(Range)

范围是Ruby中一个独特而强大的特性,用于表示一个连续的值序列。范围可以包含数字、字母甚至更复杂的对象。Ruby范围功能丰富,支持多种操作和迭代方式。本章将详细介绍Ruby中范围的创建、操作和使用方法。

🎯 范围基础

范围定义

Ruby提供了两种方式来创建范围:

ruby
# 包含结束值的范围(..)
inclusive_range = 1..5
puts inclusive_range  # 1..5
puts inclusive_range.include?(5)  # true

# 不包含结束值的范围(...)
exclusive_range = 1...5
puts exclusive_range  # 1...5
puts exclusive_range.include?(5)  # false

# 字符范围
letter_range = 'a'..'e'
puts letter_range  # "a".."e"
puts letter_range.include?('e')  # true

# 字符串范围
word_range = 'aa'..'az'
puts word_range  # "aa".."az"

# 日期范围
require 'date'
date_range = Date.new(2023, 12, 25)..Date.new(2023, 12, 31)
puts date_range  # 2023-12-25..2023-12-31

范围创建方法

ruby
# 使用字面量语法(推荐)
range1 = 1..10
range2 = 'a'..'z'

# 使用Range.new方法
range3 = Range.new(1, 10)
range4 = Range.new('a', 'z')
range5 = Range.new(1, 10, true)  # 第三个参数为true表示排除结束值

puts range1 == range3  # true
puts range2 == range4  # true
puts range5  # 1...10

📏 范围属性

范围基本属性

ruby
range = 1..10

# 获取范围的开始和结束值
puts range.begin  # 1
puts range.first  # 1 (begin的别名)
puts range.end    # 10
puts range.last   # 10 (end的别名)

# 检查是否排除结束值
puts range.exclude_end?  # false
puts (1...10).exclude_end?  # true

# 范围长度
puts range.size  # 10

# 检查范围是否为空
puts range.empty?  # false
puts (5..3).empty?  # true (无效范围)

# 转换为数组
array = range.to_a
puts array.inspect  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

范围比较

ruby
range1 = 1..5
range2 = 1..5
range3 = 2..6

# 相等性比较
puts range1 == range2  # true
puts range1 == range3  # false

# 范围包含关系
puts range1.cover?(3)   # true
puts range1.cover?(6)   # false
puts range1.include?(3) # true
puts range1.include?(6) # false

# 使用===操作符(case语句中使用)
puts range1 === 3  # true
puts range1 === 6  # false

# 范围覆盖检查
range_a = 1..10
range_b = 3..7
range_c = 8..15

puts range_a.cover?(range_b)  # true (range_a包含range_b)
puts range_a.cover?(range_c)  # false

🔍 范围操作

范围成员检查

ruby
numbers = 1..100

# 检查单个值
puts numbers.include?(50)   # true
puts numbers.include?(150)  # false

# 使用cover?方法(更高效)
puts numbers.cover?(50)     # true
puts numbers.cover?(150)    # false

# 检查另一个范围
small_range = 25..75
puts numbers.cover?(small_range)  # true

# 字符范围检查
letters = 'a'..'z'
puts letters.include?('m')  # true
puts letters.include?('A')  # false

# 字符串范围检查
words = 'aa'..'zz'
puts words.include?('ab')   # true
puts words.include?('ba')   # true
puts words.include?('aaa')  # false (超出范围)

范围边界操作

ruby
range = 10..20

# 获取范围的最小值和最大值
puts range.min  # 10
puts range.max  # 20

# 获取前几个元素
puts range.first(3).inspect  # [10, 11, 12]

# 获取后几个元素
puts range.last(3).inspect   # [18, 19, 20]

# 获取范围的中间值
def range_median(range)
  min, max = range.minmax
  (min + max) / 2.0
end

puts range_median(range)  # 15.0

# 范围的统计信息
def range_stats(range)
  {
    min: range.min,
    max: range.max,
    size: range.size,
    average: (range.min + range.max) / 2.0
  }
end

stats = range_stats(1..100)
puts stats  # {:min=>1, :max=>100, :size=>100, :average=>50.5}

🔁 范围迭代

基本迭代

ruby
# 数字范围迭代
(1..5).each { |n| print "#{n} " }
puts  # 1 2 3 4 5

# 字符范围迭代
('a'..'e').each { |char| print "#{char} " }
puts  # a b c d e

# 使用each_with_index
('x'..'z').each_with_index do |char, index|
  puts "#{index}: #{char}"
end
# 0: x
# 1: y
# 2: z

# 反向迭代
(1..5).reverse_each { |n| print "#{n} " }
puts  # 5 4 3 2 1

高级迭代

ruby
# 使用map转换
squared = (1..5).map { |n| n ** 2 }
puts squared.inspect  # [1, 4, 9, 16, 25]

# 使用select筛选
evens = (1..10).select(&:even?)
puts evens.inspect  # [2, 4, 6, 8, 10]

# 使用reject排除
odds = (1..10).reject(&:even?)
puts odds.inspect  # [1, 2, 3, 5, 7, 9]

# 使用find查找
first_large = (1..100).find { |n| n > 50 }
puts first_large  # 51

# 使用take和drop
range = 1..20
puts range.take(5).inspect   # [1, 2, 3, 4, 5]
puts range.drop(15).inspect  # [16, 17, 18, 19, 20]

分块迭代

ruby
# 使用each_slice分块处理
(1..20).each_slice(5) do |slice|
  puts slice.inspect
end
# [1, 2, 3, 4, 5]
# [6, 7, 8, 9, 10]
# [11, 12, 13, 14, 15]
# [16, 17, 18, 19, 20]

# 使用each_cons连续处理
(1..10).each_cons(3) do |cons|
  puts cons.inspect
end
# [1, 2, 3]
# [2, 3, 4]
# [3, 4, 5]
# [4, 5, 6]
# [5, 6, 7]
# [6, 7, 8]
# [7, 8, 9]
# [8, 9, 10]

# 使用cycle无限循环
# (1..3).cycle(2) { |n| print "#{n} " }
# puts  # 1 2 3 1 2 3

🎯 范围实践示例

数字范围应用

ruby
class NumberRangeUtils
  # 检查数字是否在有效范围内
  def self.valid_range?(number, min, max)
    (min..max).cover?(number)
  end
  
  # 生成指定范围内的随机数
  def self.random_in_range(range)
    range.to_a.sample
  end
  
  # 计算范围内的质数
  def self.primes_in_range(range)
    range.select { |n| prime?(n) }
  end
  
  # 计算范围内数字的和
  def self.sum_range(range)
    # 对于大范围,使用数学公式更高效
    if range.exclude_end?
      n = range.last - range.first
    else
      n = range.last - range.first + 1
    end
    n * (range.first + range.last) / 2
  end
  
  private
  
  def self.prime?(n)
    return false if n < 2
    return true if n == 2
    return false if n.even?
    
    (3..Math.sqrt(n)).step(2) do |i|
      return false if n % i == 0
    end
    true
  end
end

# 使用数字范围工具
puts NumberRangeUtils.valid_range?(15, 10, 20)  # true
puts NumberRangeUtils.valid_range?(25, 10, 20)  # false

random_number = NumberRangeUtils.random_in_range(1..100)
puts "随机数: #{random_number}"

primes = NumberRangeUtils.primes_in_range(1..30)
puts "1到30的质数: #{primes.inspect}"  # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

sum = NumberRangeUtils.sum_range(1..100)
puts "1到100的和: #{sum}"  # 5050

字符范围应用

ruby
class CharacterRangeUtils
  # 生成字母表
  def self.alphabet
    ('a'..'z').to_a
  end
  
  # 生成大写字母表
  def self.uppercase_alphabet
    ('A'..'Z').to_a
  end
  
  # 检查字符是否为字母
  def self.letter?(char)
    ('a'..'z').cover?(char.downcase)
  end
  
  # 检查字符是否为数字
  def self.digit?(char)
    ('0'..'9').cover?(char)
  end
  
  # 生成随机字母
  def self.random_letter
    ('a'..'z').to_a.sample
  end
  
  # 生成指定范围内的随机字符串
  def self.random_string(length, char_range = 'a'..'z')
    Array.new(length) { char_range.to_a.sample }.join
  end
  
  # 字符串范围检查
  def self.in_range?(string, start_str, end_str)
    (start_str..end_str).cover?(string)
  end
end

# 使用字符范围工具
puts "字母表: #{CharacterRangeUtils.alphabet.take(5).inspect}"  # ["a", "b", "c", "d", "e"]
puts "大写字母: #{CharacterRangeUtils.uppercase_alphabet.take(5).inspect}"  # ["A", "B", "C", "D", "E"]

puts CharacterRangeUtils.letter?('A')  # true
puts CharacterRangeUtils.digit?('5')   # true

random_letter = CharacterRangeUtils.random_letter
puts "随机字母: #{random_letter}"

random_string = CharacterRangeUtils.random_string(8)
puts "随机字符串: #{random_string}"

puts CharacterRangeUtils.in_range?("apple", "a", "z")  # true

日期范围应用

ruby
require 'date'

class DateRangeUtils
  # 创建日期范围
  def self.date_range(start_date, end_date)
    Date.parse(start_date)..Date.parse(end_date)
  end
  
  # 计算日期范围内的天数
  def self.days_in_range(date_range)
    (date_range.end - date_range.begin).to_i + 1
  end
  
  # 获取工作日数量
  def self.business_days(date_range)
    count = 0
    date_range.each { |date| count += 1 unless date.saturday? || date.sunday? }
    count
  end
  
  # 获取周末天数
  def self.weekend_days(date_range)
    count = 0
    date_range.each { |date| count += 1 if date.saturday? || date.sunday? }
    count
  end
  
  # 按周分组
  def self.group_by_week(date_range)
    weeks = Hash.new { |h, k| h[k] = [] }
    date_range.each do |date|
      week_number = date.cweek
      weeks[week_number] << date
    end
    weeks
  end
  
  # 获取特定星期几的日期
  def self.days_of_week(date_range, weekday)
    # 0 = 星期日, 1 = 星期一, ..., 6 = 星期六
    date_range.select { |date| date.wday == weekday }
  end
end

# 使用日期范围工具
date_range = DateRangeUtils.date_range("2023-12-01", "2023-12-31")
puts "12月天数: #{DateRangeUtils.days_in_range(date_range)}"  # 31

business_days = DateRangeUtils.business_days(date_range)
puts "工作日: #{business_days}"

weekend_days = DateRangeUtils.weekend_days(date_range)
puts "周末: #{weekend_days}"

mondays = DateRangeUtils.days_of_week(date_range, 1)
puts "12月的星期一: #{mondays.inspect}"

🔧 范围与其他数据结构

范围与数组

ruby
# 范围转换为数组
numbers = (1..10).to_a
puts numbers.inspect  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 数组转换为范围(如果可能)
def array_to_range(arr)
  return nil if arr.empty?
  min, max = arr.minmax
  (min..max) if (min..max).to_a == arr.sort
end

consecutive = [1, 2, 3, 4, 5]
puts array_to_range(consecutive)  # 1..5

non_consecutive = [1, 3, 5, 7]
puts array_to_range(non_consecutive)  # nil

# 范围与数组操作
range = 1..10
array = [5, 15, 25, 35]

# 检查数组元素是否在范围内
in_range = array.select { |n| range.cover?(n) }
puts in_range.inspect  # [5]

# 找出范围外的数组元素
out_of_range = array.reject { |n| range.cover?(n) }
puts out_of_range.inspect  # [15, 25, 35]

范围与哈希

ruby
# 使用范围作为哈希键
grade_ranges = {
  90..100 => "A",
  80...90 => "B",
  70...80 => "C",
  60...70 => "D",
  0...60 => "F"
}

def get_grade(score, grade_ranges)
  grade_ranges.each do |range, grade|
    return grade if range.cover?(score)
  end
  "Invalid"
end

puts get_grade(95, grade_ranges)  # A
puts get_grade(85, grade_ranges)  # B
puts get_grade(75, grade_ranges)  # C
puts get_grade(65, grade_ranges)  # D
puts get_grade(55, grade_ranges)  # F

# 范围分组数据
scores = [95, 87, 76, 92, 88, 73, 91, 85, 79, 94]
score_groups = scores.group_by do |score|
  case score
  when 90..100 then "优秀"
  when 80...90 then "良好"
  when 70...80 then "中等"
  when 60...70 then "及格"
  else "不及格"
  end
end

puts score_groups
# {"优秀"=>[95, 92, 91, 94], "良好"=>[87, 88, 85], "中等"=>[76, 73, 79]}

📊 范围性能优化

大范围处理

ruby
# 对于大范围,避免转换为数组
# 低效方式
# sum = (1..1_000_000).to_a.sum

# 高效方式
def sum_range_efficiently(range)
  first, last = range.first, range.last
  count = last - first + 1
  count * (first + last) / 2
end

large_sum = sum_range_efficiently(1..1_000_000)
puts "大范围和: #{large_sum}"  # 500000500000

# 使用lazy延迟计算
def process_large_range(range)
  range.lazy
    .select(&:even?)
    .map { |n| n ** 2 }
    .first(10)
    .to_a
end

result = process_large_range(1..1_000_000)
puts result.inspect  # [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]

范围搜索优化

ruby
# 使用bsearch进行二分搜索(需要排序的范围)
sorted_array = (1..1000).to_a

# 查找第一个大于等于500的元素
index = sorted_array.bsearch_index { |x| x >= 500 }
puts "索引: #{index}, 值: #{sorted_array[index]}"  # 索引: 499, 值: 500

# 查找最后一个小于等于500的元素
index = sorted_array.bsearch_index { |x| x > 500 }&.pred || (sorted_array.length - 1)
puts "索引: #{index}, 值: #{sorted_array[index]}"  # 索引: 499, 值: 500

# 范围内的高效搜索
class RangeSearch
  def self.binary_search(range, target)
    return false unless range.cover?(target)
    
    low, high = range.first, range.last
    
    while low <= high
      mid = (low + high) / 2
      return true if mid == target
      if mid < target
        low = mid + 1
      else
        high = mid - 1
      end
    end
    
    false
  end
end

puts RangeSearch.binary_search(1..1000, 500)  # true
puts RangeSearch.binary_search(1..1000, 1500) # false

🎯 范围最佳实践

1. 选择合适的范围类型

ruby
# 包含结束值的范围(用于大多数情况)
inclusive_range = 1..10
puts inclusive_range.to_a  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 不包含结束值的范围(用于数组索引等)
exclusive_range = 0...10
puts exclusive_range.to_a  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 在循环中使用
array = ['a', 'b', 'c', 'd', 'e']
(0...array.length).each { |i| puts "#{i}: #{array[i]}" }

# 在case语句中使用
def grade_letter(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

puts grade_letter(95)  # A
puts grade_letter(85)  # B

2. 安全处理范围

ruby
class SafeRangeHandler
  # 安全创建范围
  def self.safe_range(start_val, end_val, exclude_end = false)
    return nil if start_val.nil? || end_val.nil?
    return nil if start_val > end_val
    
    exclude_end ? (start_val...end_val) : (start_val..end_val)
  end
  
  # 安全范围迭代
  def self.safe_each(range, &block)
    return [] unless range.is_a?(Range)
    return [] if range.empty?
    
    range.each(&block) if block_given?
    range.to_a
  end
  
  # 范围边界验证
  def self.validate_range(range, min_bound, max_bound)
    return false unless range.is_a?(Range)
    
    range.begin >= min_bound && range.end <= max_bound
  end
end

# 使用安全范围处理
safe_range = SafeRangeHandler.safe_range(1, 10)
puts safe_range  # 1..10

invalid_range = SafeRangeHandler.safe_range(10, 1)
puts invalid_range  # nil

SafeRangeHandler.safe_each(1..5) { |n| puts "数字: #{n}" }

valid = SafeRangeHandler.validate_range(1..10, 0, 100)
puts "范围有效: #{valid}"  # 范围有效: true

3. 范围性能优化

ruby
class RangePerformance
  # 高效的范围求和
  def self.sum_range(range)
    # 使用数学公式而不是迭代
    n = range.size
    first, last = range.first, range.last
    n * (first + last) / 2
  end
  
  # 高效的范围平均值
  def self.average_range(range)
    (range.first + range.last) / 2.0
  end
  
  # 高效的范围中位数
  def self.median_range(range)
    min, max = range.minmax
    (min + max) / 2.0
  end
  
  # 范围内查找(使用cover?而不是include?)
  def self.fast_contains?(range, value)
    range.cover?(value)
  end
end

# 使用性能优化的范围操作
large_range = 1..1_000_000
puts "和: #{RangePerformance.sum_range(large_range)}"  # 500000500000
puts "平均值: #{RangePerformance.average_range(large_range)}"  # 500000.5
puts "中位数: #{RangePerformance.median_range(large_range)}"  # 500000.5

puts RangePerformance.fast_contains?(1..100, 50)  # true

4. 范围在实际应用中的使用

ruby
# 密码强度检查
class PasswordStrengthChecker
  def self.check_length(password, min_length = 8, max_length = 128)
    valid_length = (min_length..max_length).cover?(password.length)
    {
      valid: valid_length,
      message: valid_length ? "长度合适" : "密码长度应在#{min_length}-#{max_length}字符之间"
    }
  end
  
  def self.check_character_variety(password)
    has_lower = password.match?(/[a-z]/)
    has_upper = password.match?(/[A-Z]/)
    has_digit = password.match?(/\d/)
    has_special = password.match?(/[!@#$%^&*(),.?":{}|<>]/)
    
    variety_score = [has_lower, has_upper, has_digit, has_special].count(true)
    {
      score: variety_score,
      details: {
        lowercase: has_lower,
        uppercase: has_upper,
        digits: has_digit,
        special: has_special
      }
    }
  end
end

# 使用密码强度检查
password = "MyPassword123!"
length_check = PasswordStrengthChecker.check_length(password)
puts length_check  # {:valid=>true, :message=>"长度合适"}

variety_check = PasswordStrengthChecker.check_character_variety(password)
puts variety_check  # {:score=>4, :details=>{:lowercase=>true, :uppercase=>true, :digits=>true, :special=>true}}

# 年龄验证
class AgeValidator
  def self.valid_age?(age)
    (0..150).cover?(age)
  end
  
  def self.age_group(age)
    case age
    when 0..2 then "婴儿"
    when 3..12 then "儿童"
    when 13..17 then "青少年"
    when 18..59 then "成年人"
    when 60..150 then "老年人"
    else "无效年龄"
    end
  end
end

puts AgeValidator.valid_age?(25)  # true
puts AgeValidator.age_group(25)   # 成年人

📚 下一步学习

掌握了Ruby范围操作后,建议继续学习:

继续您的Ruby学习之旅吧!

本站内容仅供学习和研究使用。