🧭 编程学习站

Ruby 入门 + 进阶训练

公网版 · 手机/电脑自适应

🐣 纯新手先看这一框(30 秒秒懂)

1

Ruby 适合做什么?Web 后端(Ruby on Rails)、脚本自动化、DevOps 工具、CLI。以「开发者幸福感」和开发效率著称。

2

为什么要学它?语法接近自然语言、表达力强。想快速做网站原型、或理解「约定优于配置」思想,Ruby 极佳。

3

怎么跑起来?本站「动手实验室」选 Ruby 点运行即可;本地装 Ruby,用 ruby hello.rb

4

新手最常踩的坑?① 没有分号、靠换行;② 变量不用声明类型;③ nil 调用方法会 NoMethodError(类似空指针);④ 两个等号 == 比内容,三个 === 是 case 匹配。

1. Hello World没有分号,没有 main

puts "你好,Ruby!"        # 打印并换行
print "1 + 2 = "         # 不换行
puts 1 + 2
秒懂Ruby 脚本从上往下执行,不用包在 main 里,写完即运行。末尾分号可省略(推荐省略)。

2. 变量与一切皆对象连数字都是对象

变量不用声明类型,赋啥是啥。关键思想:一切皆对象——数字、字符串、类、nil 都能调用方法。

x = 3
puts x.class          # Integer
puts 3.odd?           # true(数字也是对象,能调方法)
name = "Ruby"
puts name.upcase      # RUBY
干货?! 是 Ruby 命名约定:返回布尔的方法常以 ? 结尾(empty?nil?),会修改自身的方法常带 !sort!)。

3. 字符串与符号 Symbol命名的两种身份

  • "字符串":可变、每次出现可能新建对象。
  • :符号:不可变、全局唯一(同名 Symbol 是同一个对象),用作名字/键。
s1 = "a"; s2 = "a"
puts s1.equal?(s2)        # false(两个不同对象)
sym1 = :name; sym2 = :name
puts sym1.equal?(sym2)    # true(同一个 Symbol)
易错点哈希键优先用 Symbol{name: "x"}),比字符串省内存且语义清晰;别在循环里狂建字符串当键。

4. 数组与哈希Ruby 的集合很顺手

arr = [1, 2, 3]
arr << 4                 # 追加,得到 [1,2,3,4]
h = { name: "Ruby", age: 30 }
puts h[:name]            # Ruby(用 Symbol 作键)
puts arr.last            # 4

5. 控制流if / unless / while

x = 10
puts "正数" if x > 0          # 后置 if(一行判断超好用)
unless x < 0                 # unless = if not
  puts "非负"
end
3.times { puts "循环" }        # 块式循环

6. 方法 def用 def ... end 定义

def greet(name)
  "你好,#{name}!"        # 字符串插值 #{},最后一个表达式自动返回
end
puts greet("Ruby")
秒懂Ruby 方法「最后一个表达式的值」就是返回值,不用写 return(当然写也行)。

7. 块 / Proc / LambdaRuby 的函数式灵魂

{...}do...end)是匿名代码块,可传给方法。区分:

  • block:最轻量,用 yield 调用,无对象身份。
  • procreturn 从外围方法返回。
  • lambda:像「真正函数」,return 只从自己返回,且严格检查参数个数。
def run
  yield 3                  # 调用传入的块
end
run { |n| puts "拿到 #{n}" }

lam = lambda { |a, b| a + b }
puts lam.call(2, 3)

8. 类与对象class / initialize / @实例变量

class Dog
  def initialize(name)
    @name = name           # @ 开头是实例变量
  end
  def bark
    "#{@name} 汪汪"
  end
end
d = Dog.new("旺财")
puts d.bark

9. 模块与混入mixin 代替多继承

Ruby 单继承,用 module + include 把一组方法「混入」类,实现多能力复用。

module Greetable
  def hi
    "嗨,我是 #{@name}"
  end
end
class Cat
  include Greetable        # 混入模块的方法
  def initialize(name); @name = name; end
end
puts Cat.new("咪咪").hi
坑与秘include 把模块方法作为实例方法混入;extend 则混入为类方法。这是 Rails 里各种「魔法」的基础。

10. 异常处理begin / rescue / ensure

begin
  puts 1 / 0
rescue ZeroDivisionError => e
  puts "出错:#{e.message}"
ensure
  puts "一定执行"
end

11. method_missing 元编程拦截未定义的方法调用

当对象收到未定义的方法,Ruby 会调 method_missing,借此能实现「动态方法名」。

class Dynamic
  def method_missing(name, *args)
    "你调用了 #{name},参数 #{args.inspect}"
  end
end
d = Dynamic.new
puts d.whatever(1, 2)     # 未定义方法也被接住
注意滥用 method_missing 会让代码难以追踪、性能下降。Rails 的 find_by_xxx 就是它的经典应用。

12. 打开类与猴子补丁运行时修改已有类

Ruby 允许重新打开任何类(包括内置类)追加方法——强大也危险。

class String
  def shout
    self.upcase + "!"
  end
end
puts "hello".shout        # HELLO!
核心风险猴子补丁会影响全局,多个库改同一个方法会互相覆盖踩踏。生产代码慎用,务必加命名空间或 refinements 限定作用域。

13. 枚举 each / map / select函数式的日常

nums = [1, 2, 3, 4, 5]
puts nums.map { |n| n * n }.inspect        # [1,4,9,16,25]
puts nums.select { |n| n.odd? }.inspect    # [1,3,5]
puts nums.inject(0) { |s, n| s + n }       # 15(累加)

14. 并发:GIL 与 RactorRuby 的并行真相

CRuby 有 全局解释器锁(GIL/GVL):同一时刻只有一个线程在执行 Ruby 代码。CPU 密集并行请用多进程;Ruby 3 的 Ractor 才提供真正的并行。

# 多线程适合 IO 等待,不适合 CPU 密集
threads = 3.times.map do
  Thread.new { sleep 0.1; "done" }
end
puts threads.map(&:value).inspect
易错点以为开多线程就能加速计算是常见误区。GIL 下多线程主要帮你在等网络/磁盘时并发,CPU 密集还是单核跑。

15. 文件 IO读写的惯用写法

# 写
File.write("a.txt", "hello")
# 读
puts File.read("a.txt")
# 逐行
File.foreach("a.txt") { |line| puts line }
干货File.write/read 一行搞定;foreach 惰性逐行读,处理大文件不占内存。

16. attr_accessor 原理揭开属性方法的魔法

attr_accessor :x 等价于自动生成 def x; @x; enddef x=(v); @x=v; end

class User
  attr_accessor :name     # 自动生成 getter + setter
end
u = User.new
u.name = "Ruby"
puts u.name
秒懂所谓「魔法」本质是元编程:用代码生成代码。attr_reader/attr_writer/attr_accessor 只是帮你少写样板。

🧪 动手实验室(点 ▶ 直接看结果)

代码已预填,打开自动跑一次;点 ▶ 运行 看结果,框内可直接改。

① Hello World

② 字符串与符号

③ 数组 map / select

④ 类与对象

⑤ 块与 yield

💡 连老师都不会教你的干货(工业级技巧)

1. 一切皆对象数字、类、nil 都响应方法。理解这点,1.odd?nil.nil? 就不奇怪了,也利于链式调用。
2. 鸭子类型不检查「是什么类」,只关心「能响应什么方法」。用 respond_to?(:save) 判断能力而非类型。
3. block/proc/lambda 区别lambda 严格参数个数、return 只退出自身;proc 参数宽松、return 退出外围方法。混用会出诡异行为。
4. 符号 vs 字符串Symbol 不可变且全局唯一,适合做键和名字;字符串可变适合内容。哈希键用 :sym 更省内存。
5. 模块 mixin单继承下用 include/extend 混入能力,是 Ruby 复用代码的王道,也是 Rails 的基石。
6. 猴子补丁风险打开类改全局会影响所有代码,多库冲突时互相覆盖。生产慎用,或限 scope 用 refinements。
7. method_missing拦截未定义方法实现动态 API(find_by_xxx),但拖慢方法查找、难调试,记得同时定义 respond_to_missing?
8. attr_accessor 原理本质是元编程生成 getter/setter,少写样板。需要只读/只写分别用 attr_reader/writer
9. 可变字符串与 frozenRuby 3 起字符串默认非冻结,但用 frozen_string_literal: true 魔法注释可让字面量冻结,省分配。
10. 枚举链each/map/select/inject 是日常主力,能替代大部分手写循环,代码更短更声明式。
11. GIL 限制CRuby 全局锁下多线程只利于 IO 并发,CPU 密集仍单核。要真并行用多进程或 Ruby 3 的 Ractor。
12. 约定优于配置Rails 靠命名约定(如 User 类对应 users 表)省掉大量配置。学 Ruby 也建议顺手学这套思想。

自测题(点击展开答案)

  • Q1:Ruby 里 3.odd? 能调用说明什么?
    A:说明一切皆对象——连整数都是对象,能响应方法。
  • Q2::name"name" 作哈希键的区别?
    A:同名 Symbol 是同一个对象(equal? 为 true),字符串每次新建。键应优先用 Symbol,省内存且唯一。
  • Q3:proclambdareturn 行为有何不同?
    A:lambda 的 return 只退出自身;proc 的 return 直接退出外围方法。参数检查也:lambda 严格、proc 宽松。
  • Q4:Ruby 多线程能加速 CPU 密集计算吗?
    A:不能。CRuby 有 GIL,同刻只有一个线程跑 Ruby 代码;多线程主要帮 IO 并发。
  • Q5:attr_accessor :x 做了什么?
    A:自动生成 x 的 getter 和 x= 的 setter,等价于手写两个方法。
  • Q6:为什么猴子补丁危险?
    A:它修改全局类,多个库改同一方法会互相覆盖,导致难以追踪的 bug;生产应限定作用域。
  • Q7:inject(0) { |s,n| s+n } 是干什么?
    A:累加(归约)。从初始值 0 开始,每次把元素 n 合并进累计值 s,最终得到总和。
本 Ruby 课程遵循 Ruby 3.x 常用子集,手机与电脑端自适应,配套本站运行器(Ruby)练习效果最佳。 公网参考:ruby-lang.org · ruby-doc.org · The Odin Project(Rails 入门)。
🛠 在线练习