Ruby 语言学习笔记:核心概念与实践
8 minute read

Ruby 是一门优雅、动态、面向对象的脚本语言,以其简洁的语法和强大的元编程能力而闻名。它由日本程序员松本行弘(Yukihiro Matsumoto,昵称 Matz)开发,旨在提高程序员的生产力,让编程变得更愉快。本文将记录我在学习 Ruby 过程中的一些核心概念和实践心得。

Ruby 的哲学

Matz 曾说过:“Ruby 就像人类的自然语言一样,是一种富有表现力的语言。Ruby 旨在让编程变得更简单、更直观,让开发者能够专注于创造,而不是被复杂的语法和工具所困扰。” 这种哲学体现在 Ruby 的设计中,例如:

  • “最少惊奇原则” (Principle of Least Surprise): Ruby 的行为应该符合大多数程序员的直觉。
  • “程序员的友好” (Programmer’s Best Friend): Ruby 提供了许多便利的工具和语法糖,使得编写代码更加高效和愉悦。

一切皆对象

在 Ruby 中,万事万物都是对象,包括数字、字符串、甚至 nil。这意味着所有的数据类型都可以调用方法。

 1# 数字是对象
 2puts 5.class # => Integer
 3puts 5.to_s # => "5"
 4
 5# 字符串是对象
 6puts "Hello".class # => String
 7puts "Hello".length # => 5
 8
 9# nil 也是对象
10puts nil.class # => NilClass
11# puts nil.some_method # => NoMethodError: undefined method `some_method' for nil:NilClass

面向对象编程

Ruby 是一个纯粹的面向对象语言。

  • 类 (Class): 用于创建对象的蓝图。

     1class Dog
     2  attr_accessor :name, :breed # attr_accessor 自动生成 getter 和 setter 方法
     3
     4  def initialize(name, breed)
     5    @name = name
     6    @breed = breed
     7  end
     8
     9  def bark
    10    puts "Woof!"
    11  end
    12
    13  def introduce
    14    puts "My name is #{@name} and I'm a #{@breed}."
    15  end
    16end
    17
    18my_dog = Dog.new("Buddy", "Golden Retriever")
    19my_dog.bark
    20my_dog.introduce
    21puts my_dog.name # => Buddy
    
    • initialize 方法是类的构造函数。
    • @ 前缀的变量是实例变量,属于对象本身。
    • attr_accessor 是一个方便的方法,用于生成读取(getter)和写入(setter)实例变量的方法。
  • 继承 (Inheritance): Ruby 使用 < 符号表示继承。

     1class Poodle < Dog
     2  def groom
     3    puts "Grooming my fancy fur!"
     4  end
     5
     6  def bark
     7    puts "Yip yip!" # 方法重写 (Overriding)
     8  end
     9end
    10
    11my_poodle = Poodle.new("Fifi", "Poodle")
    12my_poodle.bark      # => Yip yip!
    13my_poodle.introduce # => My name is Fifi and I'm a Poodle. (继承自 Dog 类)
    14my_poodle.groom     # => Grooming my fancy fur!
    
  • 模块 (Module): 模块在 Ruby 中扮演两种重要角色:

    1. 命名空间: 用于组织相关的类和方法,避免命名冲突。
    2. 混入 (Mixins): 通过 include 关键字,可以将模块中的方法“混入”到类中,实现代码的复用,类似于其他语言的多重继承(但 Ruby 不支持多重类继承)。
     1module Swimmable
     2  def swim
     3    puts "I can swim!"
     4  end
     5end
     6
     7class Duck
     8  include Swimmable # 将 Swimmable 模块的方法混入到 Duck 类
     9  def quack
    10    puts "Quack!"
    11  end
    12end
    13
    14class Person
    15  include Swimmable
    16end
    17
    18duck = Duck.new
    19duck.quack
    20duck.swim # => I can swim!
    21
    22person = Person.new
    23person.swim # => I can swim!
    

代码块和迭代器

Ruby 中大量使用代码块(Blocks)和迭代器(Iterators),这使得处理集合和执行重复性任务非常简洁。

  • 代码块 (Blocks): 传递给方法的一段未定义的方法体,通常用 {}do...end 括起来。

     1[1, 2, 3].each { |n| puts n * 2 }
     2# => 2
     3# => 4
     4# => 6
     5
     6[4, 5, 6].each do |n|
     7  puts "Number: #{n}"
     8end
     9# => Number: 4
    10# => Number: 5
    11# => Number: 6
    
    • |n| 定义了块变量。
  • 迭代器: 许多 Ruby 方法(如 each, map, select, reject)都接受代码块,并为集合中的每个元素执行该代码块。

    • each: 遍历集合,执行块。
    • map (或 collect): 遍历集合,执行块,并将块的返回值组成一个新的数组。
    • select (或 filter): 遍历集合,执行块,返回块的返回值“真”的元素组成的新数组。
    • reject: 遍历集合,执行块,返回块的返回值“假”的元素组成的新数组。
     1numbers = [1, 2, 3, 4, 5]
     2
     3doubled_numbers = numbers.map { |n| n * 2 }
     4puts doubled_numbers.inspect # => [2, 4, 6, 8, 10]
     5
     6even_numbers = numbers.select { |n| n.even? }
     7puts even_numbers.inspect # => [2, 4]
     8
     9odd_numbers = numbers.reject { |n| n.even? }
    10puts odd_numbers.inspect # => [1, 3, 5]
    

块、Proc 和 Lambda

  • : 上面提到的 {}do...end 结构。块不能被独立存储或传递,它们必须与一个方法绑定。
  • Proc: Ruby 中表示代码块的对象。你可以创建 Proc 对象并将其传递给方法,甚至在方法外调用。
    1my_proc = Proc.new { |name| puts "Hello, #{name}!" }
    2my_proc.call("Alice") # => Hello, Alice!
    3
    4def greet(greeter_proc)
    5  greeter_proc.call("Bob")
    6end
    7greet(my_proc) # => Hello, Bob!
    
  • Lambda: Lambda 也是一种 Proc,但比 Proc 更严格。Lambda 在调用时会检查参数数量,并且 return 语句只会从 Lambda 自身返回,而不会跳出外部方法。
     1my_lambda = lambda { |name| puts "Hi, #{name}!"; return "returned from lambda" }
     2result = my_lambda.call("Charlie") # => Hi, Charlie!
     3puts result # => returned from lambda
     4
     5def test_lambda_return(&block)
     6  puts "Before calling lambda"
     7  yield # 或者 block.call
     8  puts "After calling lambda"
     9end
    10
    11test_lambda_return(&my_lambda)
    12# => Before calling lambda
    13# => Hi, Charlie!
    14# => returned from lambda
    15# => After calling lambda
    
    注意:Lambda 的 return 不会跳出 test_lambda_return 方法。

元编程 (Metaprogramming)

元编程是指编写能够生成或操作其他代码的代码。Ruby 提供了强大的元编程能力。

  • 动态方法定义:

     1class MyClass
     2  def method_missing(method_name, *args, &block)
     3    puts "You called an undefined method: #{method_name} with args #{args}"
     4    # 可以根据 method_name 动态定义方法
     5    if method_name == :dynamic_method
     6      define_singleton_method(:dynamic_method) do |*dyn_args|
     7        puts "This is a dynamically created method with args: #{dyn_args}"
     8      end
     9      send(:dynamic_method, *args, &block) # 调用新定义的方法
    10    else
    11      super # 如果不是预期的动态方法,则调用父类的 method_missing
    12    end
    13  end
    14end
    15
    16obj = MyClass.new
    17obj.some_undefined_method(1, 2) # => You called an undefined method: some_undefined_method with args [1, 2]
    18obj.dynamic_method(3, 4)      # => You called an undefined method: dynamic_method with args [3, 4]
    19                              # => This is a dynamically created method with args: [3, 4]
    
    • method_missing 是一个特殊方法,当调用一个类或对象上不存在的方法时会被调用。
    • define_singleton_method 可以在运行时为对象定义方法。
  • Open Classes: Ruby 允许你