Ruby 方法
方法是把"一段可复用逻辑"打包起来的基本单位。Ruby 的方法非常灵活——支持默认参数、可变参数、关键字参数,还有独特的问号/叹号命名约定。本章把方法的所有用法讲清楚。
1. 最基本的方法定义
用 def 关键字定义,用 end 结束:
# def 关键字定义方法,end 结束
def greet
puts "Hello!"
end
# 调用:方法有参数时括号可省
greet # 输出 Hello!
greet() # 等价Ruby 调用方法时括号可以省略(只要不引起歧义),这是社区风格。所以 puts 后面直接写字符串、link_to 不带括号传参都很常见。
2. 参数与默认值
方法和大多数语言一样可以接受参数。一个关键点:Ruby 方法默认返回最后一行表达式的值,不需要写 return。
# 必选参数
def add(a, b)
a + b # 最后一行自动作为返回值(隐式返回)
end
puts add(3, 5) # 8
# 调用时括号可省(Ruby 风格)
puts add 3, 5
# 默认值:从右往左给
def greet(name, greeting = "Hello")
"#{greeting}, #{name}!"
end
puts greet("Ruby") # Hello, Ruby!
puts greet("Ruby", "Hi") # Hi, Ruby!默认参数的规则是从右往左给——有默认值的参数必须放在没有默认值的右边,否则编译器无法判断哪个参数被省略了。
3. 可变参数:splat
用 * 收集多余参数成数组,这是写"工具方法"的常用技巧。反向用在数组前,又能把数组拆开当多个参数传:
# * 收集多余参数为数组(splat)
def sum(*nums)
nums.reduce(0, :+)
end
puts sum(1, 2, 3, 4) # 10
# 还能拆开数组传参(反向 splat)
nums = [1, 2, 3]
puts sum(*nums) # 等价 sum(1, 2, 3)4. 关键字参数(Ruby 最优雅特性之一)
把 hash 当成"命名参数"用,代码可读性极高,尤其适合参数多的方法:
# 关键字参数:把 hash 当成"命名参数"
def create_user(name:, age:, role: "guest")
puts "#{name}, #{age} 岁, 角色 #{role}"
end
create_user(name: "Alice", age: 30)
# Alice, 30 岁, 角色 guest
create_user(name: "Bob", age: 25, role: "admin")
# Bob, 25 岁, 角色 admin
# ** 收集未声明的关键字参数(double splat)
def log(message, **opts)
puts message
p opts # {:user=>"alice", :ip=>"1.2.3.4"}
end
log("登录成功", user: "alice", ip: "1.2.3.4")关键字参数的最大好处是调用时不需要记参数顺序,这让它成为构造器、配置方法、Rails controller 参数的标配。双 splat **opts 则用于收集"额外"的关键字参数,常用于日志、转发。
5. 返回值与多返回值
Ruby 没有"多返回值"语法,但通过返回数组 + 多重赋值实现等价效果,写起来和 Python 几乎一样:
# Ruby 方法默认返回最后一行表达式的值(隐式返回)
def square(x)
x * x
end
# 显式 return 用于提前返回
def safe_divide(a, b)
return nil if b.zero?
a / b
end
# 多返回值用数组 + 多重赋值
def min_max(arr)
[arr.min, arr.max]
end
lo, hi = min_max([3, 1, 4, 1, 5])
puts lo, hi # 1 5建议只在最后一行用隐式返回,需要"提前返回"时再写 return。这样代码风格统一,不会出现到处都是 return 的混乱场面。
6. 问号与叹号方法(Ruby 特色)
Ruby 方法名可以以 ? 或 ! 结尾,这是社区命名约定,不是语法关键字:
# 问号方法:返回 true/false,读起来像提问
"".empty? # true
[1, 2].include?(2) # true
"abc".start_with?("a") # true
# 叹号方法:危险版——通常原地修改 receiver
name = " Alice "
name.strip # "Alice"(返回新串,name 不变)
name.strip! # "Alice"(原地改,name 现在变了)
name.strip! # nil(已经干净,无改动可做)
# 约定:? 与 ! 是名字的一部分,不是语法
# empty? 是完整方法名
# empty 是另一个方法(可能不存在)- 问号方法:返回布尔值(或 nil/false),读起来像提问。如
empty?、nil?、include?、any?。 - 叹号方法:危险版——通常会原地修改 receiver,或更易抛异常。如
save!、strip!、sort!。
新手口诀:看到 问号 想到布尔判断,看到 叹号 想到"会改动原对象或抛异常"。Rails 里 save 返回 true/false,而 save! 失败直接抛 ActiveRecord::RecordInvalid——一个安静一个吵闹。
7. self 与类方法
方法内部的 self 指向当前 receiver(调用方法的对象)。在 def 前加 self. 定义的是类方法(类似其他语言的静态方法):
# 类方法:在 def 前加 self.
class MathHelper
def self.square(x)
x * x
end
end
MathHelper.square(5) # 25
# 实例方法内部的 self 指向 receiver
class User
attr_accessor :name
def initialize(name)
@name = name
end
def say_hi
"Hi, I'm #{self.name}" # self 可省略,这里写出来更明确
end
end8. 方法作为对象
Ruby 里方法不是一等公民(不能直接当作值传递),但可以通过 method 把它变成可传递的对象:
# method(:名字) 把方法变成可传递的对象
def double(x)
x * 2
end
m = method(:double)
puts m.call(5) # 10
puts m.(5) # 10(简写)
puts m[5] # 10(再简写)
# 传给其他方法做回调
[1, 2, 3].map(&m) # [2, 4, 6]如果需要更灵活地"把代码当数据",就要用下一章的主角——块、Proc、Lambda。
小结
本章你掌握了方法的全部用法。重点记住:隐式返回是 Ruby 风格、关键字参数最优雅、? 与 ! 是约定、self 在定义类方法时表示"类自己"。下一篇我们看 Ruby 的灵魂特性——块与 yield,它是迭代器和 DSL 的基础。
← 上一篇 Ruby 控制流
下一篇 Ruby 块与 yield →