这是一个快速入门笔记,来自官网的 Ruby in Twenty Minutes

Ruby

A dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write.

Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在20 世纪90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。

交互

跟 Python 一样有交互界面,在终端使用 irb 打开。

$ irb
irb(main):001:0> "Hello World"
=> "Hello World"
irb(main):002:0>

puts

puts 是一个基本的命令,用于打印字符串,返回结果为 nil

irb(main):002:0> puts "Hello World"
Hello World
=> nil

计算器功能

IRB 就是一个基础计算器

irb(main):003:0> 3*2
=> 6
irb(main):004:0> 3**2
=> 9
irb(main):005:0> Math.sqrt(9)
=> 3.0

Modules

上面的 Math 是一个内置的数学 module。Modules 主要有两个角色,这里扮演的角度是是将功能相似的方法分组到同一名字下。

接着是点运算符,用来告诉一个接收者它所要接受的信息。

irb(main):006:0> a = 3 ** 2
=> 9
irb(main):007:0> b = 4 ** 2
=> 16
irb(main):008:0> Math.sqrt(a+b)
=> 5.0

函数

定义函数的关键字是 def 函数体以 end 结束。

irb(main):009:0> def h
irb(main):010:1> puts "Hello World!"
irb(main):011:1> end
=> nil
irb(main):012:0> h
Hello World!
=> nil
irb(main):013:0> h()
Hello World!
=> nil
irb(main):014:0>

函数的周期简短而又频繁,定义一次,使用多次。函数调用的参数是可选的,如果没有参数,调用时括号可省略。

让函数接收参数:

irb(main):014:0> def h(name)
irb(main):015:1> puts "Hello #{name}!"
irb(main):016:1> end
=> nil
irb(main):017:0> h("Don")
Hello Don!
=> nil

在字符串中预留位置

#{name},Ruby 中的在字符串中插入信息,有点类似 Swift 中的语法:print("(name)")。

irb(main):018:0> def h(name = "World")
irb(main):019:1> puts "Hello #{name.capitalize}!"
irb(main):020:1> end
=> nil
irb(main):021:0> h
Hello World!
=> nil
irb(main):022:0> h "chris"
Hello Chris!
=> nil

可以看到有参数的时候,函数的括号也是可以省略的。而且还可以定义函数的默认参数。

使用 class 关键字定义一个新的类。

irb(main):023:0> class Greeter
irb(main):024:1>   def initialize(name = "World")
irb(main):025:2>     @name = name
irb(main):026:2>   end
irb(main):027:1>   def say_hi
irb(main):028:2>     puts "Hi #{@name}!"
irb(main):029:2>   end
irb(main):030:1>   def say_bye
irb(main):031:2>     puts "Bye #{@name}, come back soon."
irb(main):032:2>   end
irb(main):033:1> end
=> nil

需要注意的是,这里的 @name 表示实例变量。

既然定义了类,我们需要创建一个对象来使用它:

irb(main):034:0> g = Greeter.new("Pat")
=> #<Greeter:0x007fc113857a60 @name="Pat">
irb(main):035:0> g.say_hi
Hi Pat!
=> nil
irb(main):036:0> g.say_bye
Bye Pat, come back soon.
=> nil

创建了对象 g 后,名字属性有了值 Pat,能不能通过对象直接访问呢?

irb(main):037:0> g.@name
SyntaxError: (irb):37: syntax error, unexpected tIVAR, expecting '('
from /usr/bin/irb:12:in `<main>'
irb(main):038:0> g.name
NoMethodError: undefined method `name' for #<Greeter:0x007fc113857a60 @name="Pat">
from (irb):38
from /usr/bin/irb:12:in `<main>'

很明显是不行的!

面向对象

实例变量是被隐藏起来的,符合面向对象的设计,将内部属性保护起来。

但也并非完全隐藏起来,可以通过检查对象看到:

irb(main):039:0> Greeter.instance_methods
=> [:say_hi, :say_bye, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]

我们定义的只有两个,这里列出的是 Greeter 对象所有的函数,即包括了它所继承的类的函数。

可以通过 false 参数,列出不包括父类的函数。

irb(main):040:0> Greeter.instance_methods(false)
=> [:say_hi, :say_bye]

接着使用父类的 respond_to 方法来测试 Greeter 对象 g 会对哪些函数作出回应:

irb(main):041:0> g.respond_to?("name")
=> false
irb(main):042:0> g.respond_to?("say_hi")
=> true
irb(main):043:0> g.respond_to?("to_s")
=> true

可见 name 还是隐藏的。

访问属性

使用 attr_accessor 让属性变成可访问。实际会自动生成两个新函数,一个用于读取,一个用于赋值。

irb(main):058:0> class Greeter
irb(main):059:1>   attr_accessor :name
irb(main):060:1> end
=> nil
irb(main):061:0> g = Greeter.new("Andy")
=> #<Greeter:0x007fc116acf100 @name="Andy">
irb(main):062:0> g.respond_to?("name")
=> true
irb(main):063:0> g.respond_to?("name=")
=> true
irb(main):064:0> Greeter.instance_methods(false)
=> [:say_hi, :say_bye, :name, :name=]

注释

使用 # 号注释,当前行的 # 号后面的内容是注释内容。

更强大的类

这次不再使用互动的 Ruby 解析器 IRB,而是将代码写到文件里。

输入 exit 或 Control-D 退出 IRB。

然后新那家一个文件 MegaGreeter.rb,输入以下内容:

#!/ur/bin/env ruby

class MegaGreeter
  attr_accessor :names

  # Create the object
  def initialize(name = "World")
    @names = names
  end

  # Say hi to everybody
  def say_hi
    if @names.nil?
      puts "..."
    elsif @names.respond_to?("each")
      # @names is a list of some kind, iterate!
      @names.each do |name|
        puts "Hello #{name}!"
      end
    else
      puts "Hello #{@names}!"
    end
  end

  # Say bye to everybody
  def say_bye
    if @names.nil?
      puts "..."
    elsif @names.respond_to?("join")
      # Join the list elements with commas
      puts "Goodbye #{@names.join(", ")}. Come back soon!"
    else
      puts "Goodbye #{@names}. Come back soon!"
    end
  end

end

if __FILE__ == $0
  mg = MegaGreeter.new
  mg.say_hi
  mg.say_bye

  # Change name to be "Zeke"
  mg.names = "Zeke"
  mg.say_hi
  mg.say_bye

  # Change the name to an array of names
  mg.names = ["Albert", "Brenda", "Charles", "Dave", "Engelbert"]
  mg.say_hi
  mg.say_bye

  # Change to nil
  mg.names = nil
  mg.say_hi
  mg.say_bye
end

使用命令 ruby MegaGreeter.rb 运行:

$ ruby MegaGreeter.rb
...
...
Hello Zeke!
Goodbye Zeke. Come back soon!
Hello Albert!
Hello Brenda!
Hello Charles!
Hello Dave!
Hello Engelbert!
Goodbye Albert, Brenda, Charles, Dave, Engelbert. Come back soon!
...
...

循环,迭代

先判断 @names 对象能否回应 each 函数,如果可以被迭代,使用 do 后面的代码块进行迭代处理。

代码块定义在两个 | 符号之间。

代码块

“如果它走起来像鸭子,叫起来像鸭子, 那它一定是鸭子”。

这种思想的好处就是不限制函数所支持的参数类别。 如果有人写了一个新的类,只要它像其他列表类一样回应 join 这个函数, 那它就可以被相应的其他函数所使用。

让脚本跑起来

调用这个类的小技术在于:

if __FILE__ == $0

__FILE__ 是一个魔法值,它存有现在运行的脚本文件的名字。

$0 是启动脚本的名字。

这句代码的意思是 “如果这是启动脚本的话…”。这允许代码作为库调用的时候不运行启动代码,而在作为执行脚本的时候调用启动代码。

EOF.