Skip to content

Ruby 变量

变量是编程中的基本概念,用于存储和操作数据。Ruby提供了多种类型的变量,每种都有其特定的作用域和用途。本章将详细介绍Ruby中的各种变量类型及其使用方法。

📦 变量类型概述

Ruby中有四种主要的变量类型:

  1. 局部变量 - 在方法或代码块内使用
  2. 实例变量 - 属于对象实例
  3. 类变量 - 属于类本身
  4. 全局变量 - 在整个程序中都可访问

🏠 局部变量

基本概念

局部变量只在定义它们的方法或代码块内可见,以小写字母或下划线开头。

ruby
# 局部变量定义
name = "张三"
age = 25
_score = 95

# 在方法中使用局部变量
def calculate_area(length, width)
  # length和width是参数局部变量
  area = length * width  # area是方法内的局部变量
  area  # 返回值
end

puts calculate_area(5, 3)  # 15

作用域

ruby
def method1
  local_var = "方法1中的局部变量"
  puts local_var
end

def method2
  # puts local_var  # 错误:无法访问其他方法的局部变量
  local_var = "方法2中的局部变量"
  puts local_var
end

method1  # 输出: 方法1中的局部变量
method2  # 输出: 方法2中的局部变量

代码块中的局部变量

ruby
# 代码块可以访问外部的局部变量
outer_var = "外部变量"

[1, 2, 3].each do |item|
  # item是代码块参数
  puts "#{outer_var}: #{item}"
end

# 输出:
# 外部变量: 1
# 外部变量: 2
# 外部变量: 3

# 使用分号分隔代码块局部变量
[1, 2, 3].each do |item; block_local|
  block_local = item * 2
  puts "#{item} * 2 = #{block_local}"
end

变量赋值和并行赋值

ruby
# 基本赋值
x = 10
y = 20

# 并行赋值
a, b = 1, 2
puts "a = #{a}, b = #{b}"  # a = 1, b = 2

# 交换变量值
a, b = b, a
puts "a = #{a}, b = #{b}"  # a = 2, b = 1

# 数组解构
first, *rest = [1, 2, 3, 4, 5]
puts "first = #{first}"    # first = 1
puts "rest = #{rest}"      # rest = [2, 3, 4, 5]

# 忽略某些值
_, second, _ = [1, 2, 3]
puts "second = #{second}"  # second = 2

🎯 实例变量

基本概念

实例变量属于特定的对象实例,以@符号开头,在对象的整个生命周期内都存在。

ruby
class Person
  def initialize(name, age)
    @name = name    # 实例变量
    @age = age      # 实例变量
  end
  
  def introduce
    puts "我是#{@name},今年#{@age}岁"
  end
  
  def have_birthday
    @age += 1       # 修改实例变量
    puts "生日快乐!现在#{@age}岁了"
  end
  
  def name
    @name           # 访问实例变量
  end
end

person = Person.new("张三", 25)
person.introduce        # 我是张三,今年25岁
person.have_birthday    # 生日快乐!现在26岁了
puts person.name        # 张三

实例变量的作用域

ruby
class Counter
  def initialize
    @count = 0        # 每个实例都有自己的@count
  end
  
  def increment
    @count += 1
  end
  
  def count
    @count
  end
  
  def reset
    @count = 0
  end
end

counter1 = Counter.new
counter2 = Counter.new

counter1.increment
counter1.increment
puts counter1.count  # 2
puts counter2.count  # 0 (不同的实例)

counter2.increment
puts counter2.count  # 1
puts counter1.count  # 2 (不受影响)

实例变量的默认值

ruby
class Example
  def initialize
    # 未初始化的实例变量默认为nil
    puts @uninitialized.inspect  # nil
  end
  
  def set_value
    @value = "已设置"
  end
  
  def get_value
    @value  # 如果未设置,返回nil
  end
end

example = Example.new
puts example.get_value.inspect  # nil

example.set_value
puts example.get_value          # 已设置

🏢 类变量

基本概念

类变量属于类本身,被该类的所有实例共享,以@@符号开头。

ruby
class Counter
  @@total_count = 0    # 类变量
  
  def initialize
    @instance_count = 0
    @@total_count += 1
  end
  
  def increment
    @instance_count += 1
  end
  
  def instance_count
    @instance_count
  end
  
  def self.total_count
    @@total_count      # 访问类变量
  end
  
  def self.reset_total
    @@total_count = 0  # 修改类变量
  end
end

# 创建实例
counter1 = Counter.new
counter2 = Counter.new
counter3 = Counter.new

puts Counter.total_count  # 3

counter1.increment
counter1.increment
puts counter1.instance_count  # 2
puts counter2.instance_count  # 0 (不同的实例变量)

# 所有实例共享类变量
puts Counter.total_count  # 3

类变量的继承

ruby
class Parent
  @@class_var = "父类变量"
  
  def self.get_class_var
    @@class_var
  end
  
  def self.set_class_var(value)
    @@class_var = value
  end
end

class Child < Parent
  # 子类共享父类的类变量
end

puts Parent.get_class_var   # 父类变量
puts Child.get_class_var    # 父类变量

# 修改类变量会影响所有类
Child.set_class_var("修改后的值")
puts Parent.get_class_var   # 修改后的值
puts Child.get_class_var    # 修改后的值

类变量的注意事项

ruby
class A
  @@value = 1
  
  def self.value
    @@value
  end
end

class B < A
  @@value = 2  # 修改父类的类变量
end

class C < A
  # 继承了被修改的值
end

puts A.value  # 2 (被B修改了)
puts B.value  # 2
puts C.value  # 2

# 这种行为可能导致意外的结果

🌍 全局变量

基本概念

全局变量在整个程序中都可访问,以$符号开头。

ruby
# 定义全局变量
$global_counter = 0

class GlobalCounter
  def increment
    $global_counter += 1
  end
  
  def self.global_count
    $global_counter
  end
end

# 在任何地方访问全局变量
puts $global_counter  # 0

counter1 = GlobalCounter.new
counter2 = GlobalCounter.new

counter1.increment
counter2.increment
counter1.increment

puts GlobalCounter.global_count  # 3
puts $global_counter             # 3

预定义全局变量

ruby
# Ruby提供了一些预定义的全局变量
puts $0        # 当前脚本的名称
puts $:        # 负载路径 ($LOAD_PATH的别名)
puts $"        # 已加载的文件列表 ($LOADED_FEATURES的别名)
puts $$        # 当前进程ID
puts $?        # 最后执行的子进程退出状态
puts $!        # 最后引发的异常
puts $@        # 最后引发异常的回溯信息

# 输入输出相关
puts $_        # gets最后读取的字符串
puts $.        # 最后读取的文件行号
puts $;        # split的默认分隔符

全局变量的替代方案

ruby
# 避免过度使用全局变量,可以使用模块常量
module AppConfig
  DEFAULT_TIMEOUT = 30
  MAX_RETRIES = 3
  DEBUG_MODE = false
  
  def self.timeout
    @timeout ||= DEFAULT_TIMEOUT
  end
  
  def self.timeout=(value)
    @timeout = value
  end
end

puts AppConfig::DEFAULT_TIMEOUT  # 30
AppConfig.timeout = 60
puts AppConfig.timeout           # 60

🔤 常量

基本概念

常量以大写字母开头,通常全部大写,用于存储不应该改变的值。

ruby
# 定义常量
PI = 3.14159
MAX_SIZE = 100
DEFAULT_NAME = "匿名用户"

class MathConstants
  E = 2.71828
  GOLDEN_RATIO = 1.61803
  
  def self.calculate_circle_area(radius)
    PI * radius * radius
  end
end

puts PI                          # 3.14159
puts MathConstants::E            # 2.71828
puts MathConstants.calculate_circle_area(5)  # 78.53975

常量的作用域

ruby
OUTER_CONSTANT = "外部常量"

class MyClass
  INNER_CONSTANT = "内部常量"
  
  def self.show_constants
    puts OUTER_CONSTANT      # 可以访问外部常量
    puts INNER_CONSTANT      # 可以访问内部常量
  end
end

puts OUTER_CONSTANT          # 外部常量
# puts INNER_CONSTANT        # 错误:无法直接访问类内部常量
puts MyClass::INNER_CONSTANT # 通过类名访问

MyClass.show_constants       # 显示两个常量

重新赋值常量

ruby
# Ruby允许重新赋值常量,但会发出警告
WARNING_LEVEL = 1
puts WARNING_LEVEL  # 1

WARNING_LEVEL = 2   # 警告: already initialized constant WARNING_LEVEL
puts WARNING_LEVEL  # 2

# 冻结对象防止修改
FROZEN_ARRAY = [1, 2, 3].freeze
# FROZEN_ARRAY << 4  # RuntimeError: can't modify frozen Array

🔄 变量作用域详解

嵌套作用域

ruby
def outer_method
  outer_var = "外部方法变量"
  
  def inner_method
    # 不能访问outer_var
    # puts outer_var  # 错误
    inner_var = "内部方法变量"
  end
  
  inner_method
  # puts inner_var    # 错误:无法访问内部方法的变量
end

# 代码块作用域
def block_scope_example
  outer_value = "外部值"
  
  [1, 2, 3].each do |item|
    block_var = "块变量"
    puts "#{outer_value} - #{item} - #{block_var}"
  end
  
  # puts block_var  # 错误:无法访问块变量
end

block_scope_example
# 输出:
# 外部值 - 1 - 块变量
# 外部值 - 2 - 块变量
# 外部值 - 3 - 块变量

变量查找顺序

ruby
global_var = "全局变量"

class MyClass
  @@class_var = "类变量"
  
  def initialize
    @instance_var = "实例变量"
  end
  
  def method_with_local
    local_var = "局部变量"
    
    # 变量查找顺序:局部变量 -> 实例变量 -> 类变量 -> 全局变量
    puts local_var      # 局部变量
    puts @instance_var  # 实例变量
    puts @@class_var    # 类变量
    # puts global_var   # 错误:无法直接访问方法外部的局部变量
  end
end

obj = MyClass.new
obj.method_with_local

🛡️ 变量安全和最佳实践

变量命名约定

ruby
# 好的命名约定
user_name = "张三"           # 局部变量
@user_email = "user@example.com"  # 实例变量
@@active_users = 0           # 类变量
$global_config = {}          # 全局变量
MAX_LOGIN_ATTEMPTS = 3       # 常量

# 避免的命名
userName = "李四"            # 驼峰命名(不推荐)
UserName = "王五"            # 看起来像类名
user_name_ = "赵六"          # 末尾下划线(不清晰)

变量初始化

ruby
class SafeVariableExample
  def initialize
    @initialized_var = "已初始化"
    @lazy_var = nil          # 明确初始化为nil
  end
  
  def get_lazy_var
    # 惰性初始化
    @lazy_var ||= expensive_computation
  end
  
  private
  
  def expensive_computation
    puts "执行昂贵的计算..."
    "计算结果"
  end
end

变量验证

ruby
class ValidatedVariables
  def initialize(name, age)
    self.name = name  # 通过setter方法验证
    self.age = age
  end
  
  def name=(name)
    raise ArgumentError, "姓名不能为空" if name.nil? || name.empty?
    @name = name
  end
  
  def age=(age)
    raise ArgumentError, "年龄必须是正数" unless age.is_a?(Integer) && age > 0
    @age = age
  end
  
  attr_reader :name, :age
end

# 使用示例
begin
  person = ValidatedVariables.new("张三", 25)
  puts "创建成功: #{person.name}, #{person.age}"
  
  # person.age = -5  # 会引发ArgumentError
rescue ArgumentError => e
  puts "错误: #{e.message}"
end

🧪 变量实践示例

配置管理类

ruby
class ConfigManager
  @@configs = {}      # 类变量存储所有配置
  @@default_config = {  # 默认配置
    timeout: 30,
    retries: 3,
    debug: false
  }
  
  def initialize(config_name)
    @config_name = config_name
    @@configs[@config_name] = @@default_config.dup
  end
  
  def set(key, value)
    @@configs[@config_name][key] = value
  end
  
  def get(key)
    @@configs[@config_name][key]
  end
  
  def self.get_config(config_name)
    @@configs[config_name]
  end
  
  def self.list_configs
    @@configs.keys
  end
end

# 使用配置管理器
db_config = ConfigManager.new("database")
api_config = ConfigManager.new("api")

db_config.set(:host, "localhost")
db_config.set(:port, 5432)

api_config.set(:base_url, "https://api.example.com")
api_config.set(:timeout, 60)

puts db_config.get(:host)    # localhost
puts api_config.get(:timeout) # 60
puts ConfigManager.list_configs.inspect  # ["database", "api"]

计数器类

ruby
class AdvancedCounter
  @@total_counters = 0    # 总计数器数量
  @@global_count = 0      # 全局计数
  
  def initialize(name)
    @name = name
    @count = 0
    @@total_counters += 1
  end
  
  def increment(step = 1)
    @count += step
    @@global_count += step
    self  # 返回self支持链式调用
  end
  
  def decrement(step = 1)
    @count -= step
    @@global_count -= step
    self
  end
  
  def count
    @count
  end
  
  def name
    @name
  end
  
  def reset
    @@global_count -= @count
    @count = 0
    self
  end
  
  def self.total_counters
    @@total_counters
  end
  
  def self.global_count
    @@global_count
  end
  
  def self.reset_all
    @@global_count = 0
  end
  
  def info
    "#{@name}: #{@count}"
  end
end

# 使用高级计数器
counter1 = AdvancedCounter.new("计数器1")
counter2 = AdvancedCounter.new("计数器2")

counter1.increment(5).increment(3)  # 链式调用
counter2.increment(2).decrement(1)

puts counter1.info  # 计数器1: 8
puts counter2.info  # 计数器2: 1
puts "全局计数: #{AdvancedCounter.global_count}"  # 全局计数: 9
puts "总计数器: #{AdvancedCounter.total_counters}" # 总计数器: 2

🎯 变量使用最佳实践

1. 最小化作用域

ruby
# 好的做法:在需要时才定义变量
def process_users(users)
  users.map do |user|
    # processed_user只在map块中使用
    processed_user = user.dup
    processed_user[:processed_at] = Time.now
    processed_user
  end
end

# 避免:过早定义变量
def bad_example(users)
  processed_user = nil  # 不必要的早期定义
  results = []
  
  users.each do |user|
    processed_user = user.dup
    processed_user[:processed_at] = Time.now
    results << processed_user
  end
  
  results
end

2. 使用描述性名称

ruby
# 好的命名
user_authentication_token = "abc123"
maximum_retry_attempts = 3
database_connection_timeout = 30

# 避免模糊命名
token = "abc123"
max_retries = 3
timeout = 30

3. 合理使用不同类型的变量

ruby
class UserService
  @@active_users = Set.new    # 类变量:跟踪所有活跃用户
  DEFAULT_PAGE_SIZE = 20      # 常量:默认分页大小
  
  def initialize(current_user)
    @current_user = current_user  # 实例变量:当前用户
  end
  
  def list_users(page = 1)
    page_size = DEFAULT_PAGE_SIZE  # 局部变量:分页大小
    offset = (page - 1) * page_size
    
    # 查询逻辑...
  end
end

📚 下一步学习

掌握了Ruby变量后,建议继续学习:

继续您的Ruby学习之旅吧!

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