1. 变量与类型
Ruby 是动态类型语言,变量无需声明类型。Ruby 通过命名约定区分变量的作用域:
name = "Alice" # 局部变量(小写字母或下划线开头)
@age = 30 # 实例变量(属于当前对象)
@@count = 0 # 类变量(属于类及其所有实例)
$debug = true # 全局变量(整个程序可见,慎用)
MAX_SIZE = 100 # 常量(大写字母开头,修改会警告)
puts name.class # => String
puts @age.class # => Integer
puts $debug.class # => TrueClass
puts MAX_SIZE.class # => Integer
Ruby 的基本类型都是对象,没有原始类型(primitive)的概念:
42.class # => Integer
3.14.class # => Float
"hello".class # => String
:name.class # => Symbol
true.class # => TrueClass
false.class # => FalseClass
nil.class # => NilClass
42.even? # => true(数字也有方法)
-5.abs # => 5
nil.nil? # => true
"hello".is_a?(String) # => true
一切皆对象:在 Ruby 中,42、nil、true 都是对象实例。42.times { ... } 这种写法完全合法。这是 Ruby 与 Java/PHP 最根本的区别之一。
🔄 变量声明对比
| 语言 | 变量声明 |
| Ruby | name = "Alice" — 直接赋值,无关键字 |
| Python | name = "Alice" — 与 Ruby 相同 |
| PHP | $name = "Alice"; — 需要 $ 前缀和分号 |
| Java | String name = "Alice"; — 需要类型声明 |
2. 字符串
Ruby 的字符串功能丰富,支持插值、heredoc 和大量内置方法。
单引号 vs 双引号
name = "Ruby"
puts "Hello, #{name}!" # => Hello, Ruby!(双引号支持插值)
puts 'Hello, #{name}!' # => Hello, #{name}!(单引号原样输出)
puts "2 + 3 = #{2 + 3}" # => 2 + 3 = 5(插值内可以写任意表达式)
puts "换行符:\n第二行" # 双引号支持转义字符
puts '不转义:\n 原样输出' # 单引号不处理转义(\' 和 \\ 除外)
Heredoc 多行字符串
html = <<~HTML
<div class="card">
<h1>#{name}</h1>
<p>版本 3.3</p>
</div>
HTML
# <<~ 会自动去除公共缩进
常用字符串方法
s = " Hello, Ruby World! "
s.length # => 23
s.strip # => "Hello, Ruby World!"
s.upcase # => " HELLO, RUBY WORLD! "
s.downcase # => " hello, ruby world! "
s.include?("Ruby") # => true
s.gsub("Ruby", "Crystal") # => " Hello, Crystal World! "
s.split(", ") # => [" Hello", "Ruby World! "]
s.chars # => [" ", " ", "H", "e", ...]
s.reverse # => " !dlroW ybuR ,olleH "
s.start_with?(" H") # => true
s.freeze # 冻结后不可修改
Symbol vs String
:name.class # => Symbol
"name".class # => String
:name.object_id == :name.object_id # => true(同名 Symbol 是同一对象)
"name".object_id == "name".object_id # => false(每次创建新 String 对象)
# Symbol 适合做 Hash 键和标识符,String 适合处理文本数据
冻结字符串字面量:在文件头部添加 # frozen_string_literal: true 魔法注释,所有字符串字面量自动冻结为不可变对象,减少内存分配,提升性能。这是 Ruby 社区的推荐做法。
3. 数组 (Array)
Ruby 数组是有序、可变长的集合,元素类型可以混合。
创建数组
a = [1, "two", 3.0, :four, nil]
b = Array.new(3, 0) # => [0, 0, 0]
c = Array.new(5) { |i| i * 2 } # => [0, 2, 4, 6, 8]
words = %w[apple banana cherry] # => ["apple", "banana", "cherry"]
symbols = %i[get post put delete] # => [:get, :post, :put, :delete]
常用方法
arr = [3, 1, 4, 1, 5, 9, 2, 6]
arr.push(7) # => [3, 1, 4, 1, 5, 9, 2, 6, 7]
arr << 8 # => [..., 7, 8](<< 等同于 push)
arr.pop # => 8,移除并返回末尾元素
arr.shift # => 3,移除并返回首元素
arr.unshift(0) # 在头部插入
arr = [3, 1, 4, 1, 5, 9, 2, 6]
arr.sort # => [1, 1, 2, 3, 4, 5, 6, 9]
arr.uniq # => [3, 1, 4, 5, 9, 2, 6]
arr.flatten # 展平嵌套数组
arr.compact # 移除所有 nil 元素
arr.sample # 随机取一个元素
arr.count # => 8
函数式操作
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
numbers.map { |n| n ** 2 } # => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
numbers.select { |n| n.even? } # => [2, 4, 6, 8, 10]
numbers.reject { |n| n > 5 } # => [1, 2, 3, 4, 5]
numbers.reduce(0) { |sum, n| sum + n } # => 55
numbers.reduce(:+) # => 55(符号简写)
numbers.each_slice(3).to_a # => [[1,2,3], [4,5,6], [7,8,9], [10]]
numbers.min # => 1
numbers.max # => 10
numbers.sum # => 55
数组切片
arr = [10, 20, 30, 40, 50]
arr[0] # => 10
arr[-1] # => 50(负索引从末尾计算)
arr[1..3] # => [20, 30, 40](包含末尾)
arr[1...3] # => [20, 30](不包含末尾)
arr[1, 2] # => [20, 30](从索引1取2个元素)
arr.first(3) # => [10, 20, 30]
arr.last(2) # => [40, 50]
4. 哈希 (Hash)
Hash 是 Ruby 的键值对集合,类似 Python 的 dict、Java 的 HashMap、PHP 的关联数组。
# 旧语法(火箭操作符)
old_style = { "name" => "Ruby", "year" => 1995 }
# 新语法(Symbol 键推荐写法,Ruby 1.9+)
person = { name: "Matz", age: 69, lang: "Ruby" }
# 创建带默认值的 Hash
counter = Hash.new(0)
counter[:apples] += 1 # 不存在的键返回默认值 0,而非 nil
常用方法
h = { name: "Ruby", version: 3.3, typed: false }
h[:name] # => "Ruby"
h.fetch(:name) # => "Ruby"(键不存在时抛异常)
h.fetch(:missing, "N/A") # => "N/A"(提供默认值)
h.keys # => [:name, :version, :typed]
h.values # => ["Ruby", 3.3, false]
h.key?(:version) # => true
h.merge(year: 1995) # => { name: "Ruby", version: 3.3, typed: false, year: 1995 }
h.each do |key, value|
puts "#{key}: #{value}"
end
h.map { |k, v| [k, v.to_s] }.to_h
h.select { |k, v| v.is_a?(Numeric) } # => { version: 3.3 }
h.transform_values(&:to_s) # => { name: "Ruby", version: "3.3", typed: "false" }
嵌套访问(dig)
data = {
user: {
profile: {
name: "Alice",
address: { city: "Tokyo" }
}
}
}
data[:user][:profile][:name] # => "Alice"
data.dig(:user, :profile, :address, :city) # => "Tokyo"
data.dig(:user, :settings, :theme) # => nil(安全访问,不抛异常)
🔄 字典/映射对比
| 语言 | 类型 | 创建示例 |
| Ruby | Hash | { name: "v" } |
| Python | dict | {"name": "v"} |
| PHP | array(关联) | ["name" => "v"] |
| Java | HashMap | Map.of("name", "v") |
5. 控制流
if / elsif / else
score = 85
if score >= 90
grade = "A"
elsif score >= 80
grade = "B"
elsif score >= 70
grade = "C"
else
grade = "D"
end
puts grade # => "B"
# if 是表达式,可以直接赋值
grade = if score >= 90 then "A"
elsif score >= 80 then "B"
else "C"
end
unless 与后缀条件
logged_in = false
unless logged_in
puts "请先登录"
end
# 后缀条件 —— Ruby 的标志性语法糖
puts "欢迎回来" if logged_in
puts "请先登录" unless logged_in
# 三元运算符
status = logged_in ? "在线" : "离线"
case / when
lang = "Ruby"
case lang
when "Ruby"
puts "优雅的脚本语言"
when "Python", "PHP"
puts "流行的脚本语言"
when /^Java/
puts "以 Java 开头的语言"
else
puts "其他语言"
end
# case 也是表达式
desc = case lang
when "Ruby" then "💎 优雅"
when "Python" then "🐍 简洁"
else "未知"
end
模式匹配(Ruby 3.x)
data = { name: "Alice", role: "admin", scores: [95, 88, 72] }
case data
in { name: String => name, role: "admin" }
puts "管理员:#{name}"
in { name: String => name, scores: [Integer => first, *] }
puts "#{name} 的第一次成绩:#{first}"
end
# 数组模式匹配
case [1, 2, 3]
in [Integer => a, Integer => b, Integer => c] if a < b
puts "递增序列:#{a}, #{b}, #{c}"
end
case...in vs case...when:case...when 使用 === 操作符匹配,case...in 是 Ruby 3.0 引入的结构化模式匹配,可以解构嵌套数据结构,功能更强大。
循环
# while
i = 0
while i < 5
puts i
i += 1
end
# until(while 的反义词)
j = 10
until j <= 0
j -= 3
end
# for..in(较少使用,Ruby 社区偏好 each)
for n in [10, 20, 30]
puts n
end
# Ruby 惯用写法
5.times { |i| puts i } # 0, 1, 2, 3, 4
1.upto(5) { |i| puts i } # 1, 2, 3, 4, 5
10.downto(1) { |i| puts i } # 10, 9, ..., 1
(1..10).step(2) { |i| puts i } # 1, 3, 5, 7, 9
["a", "b", "c"].each { |ch| puts ch }
["a", "b", "c"].each_with_index { |ch, i| puts "#{i}: #{ch}" }
# 循环控制
[1, 2, 3, 4, 5].each do |n|
next if n == 3 # 跳过当前迭代(类似 continue)
break if n == 5 # 终止循环
puts n
end
6. 异常处理
begin
result = 10 / 0
rescue ZeroDivisionError => e
puts "除零错误:#{e.message}"
rescue StandardError => e
puts "其他错误:#{e.message}"
ensure
puts "无论如何都会执行(类似 finally)"
end
主动抛出异常
def divide(a, b)
raise ArgumentError, "除数不能为零" if b.zero?
a.to_f / b
end
begin
divide(10, 0)
rescue ArgumentError => e
puts e.message # => "除数不能为零"
end
自定义异常
class InsufficientFundsError < StandardError
attr_reader :amount
def initialize(amount)
@amount = amount
super("余额不足,缺少 #{amount} 元")
end
end
def withdraw(balance, amount)
raise InsufficientFundsError.new(amount - balance) if amount > balance
balance - amount
end
begin
withdraw(100, 250)
rescue InsufficientFundsError => e
puts e.message # => "余额不足,缺少 150 元"
puts e.amount # => 150
end
retry 重试机制
attempts = 0
begin
attempts += 1
puts "尝试第 #{attempts} 次..."
raise "网络超时" if attempts < 3
puts "请求成功"
rescue RuntimeError
retry if attempts < 3
puts "重试次数已用完"
end
🔄 异常处理对比
| 语言 | 捕获 | 抛出 | 清理 |
| Ruby | begin/rescue |
raise |
ensure |
| Python | try/except |
raise |
finally |
| PHP | try/catch |
throw |
finally |
| Java | try/catch |
throw |
finally |
Ruby 独有 retry 关键字,可在 rescue 块中自动重试 begin 块,非常适合网络请求等可重试操作。
7. 本章要点
📌 变量与类型
局部变量无前缀,@ 实例变量,@@ 类变量,$ 全局变量,大写常量。一切皆对象,用 .class 查看类型。
📝 字符串
双引号支持 #{} 插值和转义,单引号原样输出。Symbol 是不可变的标识符,适合做 Hash 键。
📋 数组
map、select、reduce 等函数式方法是 Ruby 数组的核心操作。%w[] 快速创建字符串数组。
🗂️ Hash
{ key: value } 是 Symbol 键简写。dig 安全访问嵌套结构,merge 合并 Hash。
🔀 控制流
unless 是 if 的反义词,后缀条件是 Ruby 特色。case...in 支持强大的模式匹配。
⚠️ 异常处理
begin/rescue/ensure 结构,raise 抛出异常,retry 重试。自定义异常继承 StandardError。
掌握了基础语法后,下一章将学习 方法与面向对象编程 —— Ruby 的方法、Block/Proc/Lambda 和类系统是其最强大的特性。