CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Ruby 语言入门/课时/语法基础
RawBlame
课时目录12 节

Ruby 语言入门

  1. 01Ruby 环境
  2. 02数据类型
  3. 03语法基础
  4. 04控制流
  5. 05方法与 Blocks
  6. 06OOP 与 Mixin
  7. 07元编程
  8. 08IO 与 Enumerable
  9. 09并发
  10. 10Gems 与框架
  11. 11测试与质量
  12. 12综合练习

语法基础

运算符与命名。

30 分钟

语法基础

这一课你将能做到

  1. 正确声明和使用局部变量、实例变量、类变量、全局变量和常量,说清各自的可见范围
  2. 掌握 Ruby 的运算符优先级、三目表达式、||= 赋值风格以及 safe navigation 操作符 &.
  3. 写出符合 Ruby 社区命名规范(snake_case、?/ ! 方法后缀)的代码

为什么要学

Ruby 语法表面看和 Python 很像,但有自己的一套规则——变量前缀 $/@/@@ 一眼就能看出作用域;||= 是处理默认值的惯用语法;safe navigation &. 防止 NPE 比 Java 更优雅。这些语法糖写对了极爽,写错了调试到怀疑人生。

人话:语法是语言的交通规则。你只有认了 def/end 之间的红绿灯、分得清 @name 和 name 的区别,才不至于一上路就撞。

先建立图像

变量作用域速查
┌─────────────────┬────────────┬──────────────────────┐
│ 变量类型          │ 前缀         │ 可见范围               │
├─────────────────┼────────────┼──────────────────────┤
│ 局部变量           │ 无前缀       │ 方法/块内部             │
│ 实例变量           │ @name       │ 单个对象内              │
│ 类变量             │ @@count     │ 整个类及子类(少用)     │
│ 全局变量           │ $debug      │ 全局(极少用)           │
│ 常量               │ CONST_NAME  │ 所在类/模块及外层         │
└─────────────────┴────────────┴──────────────────────┘

跟着做

步骤 1:变量与常量声明

# 局部变量:小写/下划线开头,只活在当前作用域
score = 88
puts score  # => 88

# 实例变量:@,在类的方法之间共享
class Player
  def initialize(name)
    @name = name
  end
  def greet
    "我是 #{@name}"
  end
end
p = Player.new("阿强")
puts p.greet  # => 我是 阿强

# 类变量:@@,类和所有实例共享,但子类继承可能踩坑,建议用类实例变量代替
class Counter
  @@total = 0
  def self.total
    @@total
  end
  def self.increment!
    @@total += 1
  end
end
Counter.increment!
puts Counter.total  # => 1

# 全局变量:$ 开头,所有地方可见,能不用就不用
$log_level = :info
puts $log_level

# 常量:全大写,定义后不能重新赋值(会报警告)
PI = 3.1415926
puts PI
# PI = 3  # => warning: already initialized constant PI

步骤 2:运算符与条件表达式

# ||= 惯用法:变量为 nil/false 时才赋值
config = nil
config ||= { theme: "dark" }
puts config.inspect   # => {:theme=>"dark"}
config ||= { theme: "light" }
puts config.inspect   # => {:theme=>"dark"}  已被赋值过,不覆盖

# safe navigation:对象的链式调用里防 nil
user = { profile: { name: "Lisa" } }
puts user[:profile]&.fetch(:name, "未知")   # => Lisa
puts user[:ghost]&.fetch(:name, "未知")     # => nil(如果没 & 会抛 NoMethodError)

# 三目表达式
age = 20
status = age >= 18 ? "成年" : "未成年"
puts status

# 区间判断(include)
puts (1..10).include?(5)   # => true
puts ("a".."z").cover?("m") # => true,cover? 更快,不做范围转换

步骤 3:命名约定与条件语句

# 命名:snake_case
snake_case_variable = 1
my_method_name      = ->(x){ x + 1 }

# 方法后缀:
# ?  返回 boolean(命名约定)
# !  有副作用或更危险(命名约定)
def palindrome?(s)
  s == s.reverse
end
puts palindrome?("racecar")  # => true
puts palindrome?("hello")    # => false

# if / unless 后置(一行时推荐)
puts "成年" if age >= 18
puts "未成年" unless age >= 18

# case 语句(Ruby 里不是 switch,是 case-when)
grade = "B"
level = case grade
        when "A" then "优秀"
        when "B" then "良好"
        when "C" then "及格"
        else "不及格"
        end
puts level   # => 良好

步骤 4:parallel assignment 与解构

# 并行赋值
a, b = 1, 2
a, b = b, a     # 优雅地交换值,不用临时变量
puts "a=#{a}, b=#{b}"

# 数组解构,多余元素归到尾部
first, second, *rest = [10, 20, 30, 40]
puts "first=#{first}, rest=#{rest.inspect}"   # => first=10, rest=[30, 40]

# 忽略不需要的变量用下划线
a, _, c = [1, 2, 3]
puts a, c   # => 1, 3

讲清楚要点

  1. 变量前缀就是作用域身份证:没有前缀是局部变量,@ 是当前对象实例变量,@@ 是类变量,$ 是全局变量。看到前缀就知道它从哪来、活到哪。
  2. ||= 是 Ruby 最常用的默认值写法:cache ||= expensive_compute 意为「如果 cache 还没值(nil/false),就赋上」。用熟了会写出极其紧凑的初始化代码。
  3. &. safe navigation 是 Ruby 2.3 引入:相当于 Java 的 Optional 链式调用或 Groovy 的 ?.。user&.name 在 user 为 nil 时安全返回 nil,而不是抛 NoMethodError。
  4. case-when 不是 switch:它隐式调用 ===(case equality),所以 when 1..5 能匹配区间;when String 能匹配类型。这让它比 C 语言的 switch 灵活得多。
  5. 常量其实可变:CONST = 1 后再次赋值只会发 warning,不会报错。Ruby 的常量更像个强提示,不是锁。

你可能会懵的地方

  • 为什么 Ruby 不用花括号 {} 而是 end? Ruby 也支持 { },但只用于代码块(block)和单行哈希。类/方法/if/case 全部用 end。读代码时数 def 和 end 配对。

  • 局部变量和零参数方法调用长得一样? name 可能是局部变量,也可能是调用 name() 方法。Ruby 在解析时优先视为变量;如果有同名的无参数方法,前面加 self. 显式调用:self.name。

  • ||= 在 boolean 为 false 时会不会赋值? 会。flag = false; flag ||= true 后 flag 变成 true——因为 false 在 || 里算假。想要仅 nil 时赋值:flag = nil if flag.nil?。

  • and/or vs &&/|| 的区别? 优先级不同:and/or 优先级极低,适合控制流:open_file and process。日常比较运算用 &&/||,避免优先级陷阱。

  • === 是什么黑魔法? 在 case-when 里,when expr 等价于 expr === target。所以 Integer 类的 === 实现是「判断目标是不是这个类的实例」,区间 (1..5) 的 === 是「目标是否落在区间内」。

错法对照

错法问题正解
A = 1; a = 2 以为一样Ruby 大小写敏感,A 和 a 是不同变量常量全大写,局部变量全小写
if score = 90赋值返回 90 永远真,打错一个 =if score == 90 双等号比较
@name 在类外面直接用外部访问不到实例变量用 attr_reader :name 暴露 getter
CONST = [1,2]; CONST << 3常量引用不能改,但引用的内容可变冻结数组:CONST = [1,2].freeze
user.profile.name 链式调用 user 可能 nilNoMethodError on niluser&.profile&.name

卡点急救

  1. undefined local variable or method:要么变量没定义,要么和方法重名 Ruby 当变量解析了。检查拼写、是否在前面赋值过。
  2. warning: already initialized constant:常量重复赋值,虽然能跑但不安全。把它提取成配置类或冻结对象。
  3. Unexpected end / 少 end:Ruby 是 def ... end、class ... end、if ... end 层层嵌套。VS Code 括号彩虹 + 缩进整理一眼解决。

面试常问

  • =、==、===、equal?、eql? 的区别? = 赋值;== 值比较,Integer 和 Float 可能相等;=== case equality,由 when 右侧调用;equal? 对象身份;eql? 严格值+类型比较(Hash 键)。

  • @ 和 @@ 何时用?为什么不推荐 @@? @ 实例变量存对象状态;@@ 类变量跨实例共享,但子类继承时会共享同一个变量,容易污染。现代 Ruby 建议用类实例变量 @count + 类方法访问。

  • ||= 的原理和坑? a ||= b 等价 a || a = b,不是 a = a || b。如果 a 未定义,Ruby 默认视为 nil,所以不会报错。坑:boolean false 会被重新赋值。

自测三问

  1. @name、name、@@name、$name 各自的作用域和访问方式是什么?
  2. &. 和 Java 的 Optional 链式调用有什么异同?
  3. case-when 里 when String 为什么能匹配字符串类型?

小结一句话

Ruby 语法的基础是「前缀即作用域、命名即意图、运算符都有返回值」,把这套规则用熟,代码就写对了。

延伸

  • Ruby 语法速查
  • 下一课:控制流——把分支和循环玩出 Ruby 的味道。
加载本节练习…
← 上一课数据类型下一课 →控制流