Ruby
基础
命令行
ruby <ruby源文件>			  # 运行
ruby -e "puts RUBY_VERSION"	# 直接执行一段代码
ruby -c <ruby源文件>		  # 检查语法,不执行代码Linux下ruby源文件第一行指明解释器的路径:
#!/usr/bin/ruby基本语法
Ruby把分号和换行符解释为语句的结尾(但是,如果在行尾遇到运算符,比如
+、-或\等,它们表示一个语句的延续);Ruby的标识符是大小写敏感的,标识符的名称可以包含字母、数字和下划线。
注释:可以单行和多行注释。
# 我是注释,请忽略我。 =begin 这是注释。 这也是注释。 =end代码块:可以使用
{}字符或do和end关键字来分隔代码块。在if语句中,代码块由then和end关键字定界,then可以省略。
输入输出
这些方法其实都是
Kernel类的方法,只是一般省略了。
puts输出会换行;print不会换行;p输出对象的原始形式(如字符串带引号,数组带括号);gets用于读取一行字符串。
print "What is your name? "
name = gets
puts "Hello #{name}"变量
常量以大写字母开头。原则上不应该修改常量的值,如果修改了会报warning,但能修改成功。
变量以小写字母或下划线开头。变量前的特殊字符表示作用域。
局部变量
变量前没有特殊字符表示。
def method1
    x = 10
    puts local_variables	# 输出:x
end
method1
p x		# 报错全局变量
$开头,在任何地方都有效。
$gb = 5
def method1
    puts $gb
    puts global_variables.include? :$gb	# 加:表示符号本身,输出true
end
method1实例变量、类变量
实例变量@开头,仅在一个对象内部有效;类变量@@开头,该类的所有实例共享该变量。
class Being
    @@count = 0
    def initialize name
        @name = name
        @@count = @@count + 1
    end
    def output
        puts "#{@name} #{@@count}"
        # puts instance_variables 可以打印实例变量,输出:@name
    end    
end
b1 = Being.new "Being 1"
b2 = Being.new "Being 2"
b3 = Being.new "Being 3"
b1.output		# Being 1, 3
b2.output		# Being 2, 3
b3.output		# Being 3, 3伪变量
看起来像变量,但不能赋值或修改。
self:表示当前对象。puts self # main class MyClass puts self # MyClass endnil:类似其他语言的null,是NilClass的唯一实例。puts nil.class # NilClasstrue和false:true是TrueClass的实例,false是FalseClass的实例。puts true.class # TrueClass puts false.class # FalseClassRuby 中,只有
false和nil被视为“假”,其他所有值(包括0和空字符串"")都为“真”。
预定义变量
$0存储当前脚本名称;$*变量存储命令行参数;$$存储脚本的PID。
$ ruby test.rb aaa bbb
test.rb
aaa
bbb
14884BEGIN和END语句
BEGIN中的代码最先执行,END中的代码最后执行。
puts "这是主 Ruby 程序"
BEGIN {
   puts "初始化 Ruby 程序"
}
END {
   puts "停止 Ruby 程序"
}数据类型
布尔值(TrueClass、FalseClass)
即true和false。
符号(Symbol)
不可变的轻量字符串(有时我们不需要字符串对象的全部功能,通常用于枚举对象、哈希的键等)。
所有符号都存储在符号表中(可以打印Symbol.all_symbols查看)。
puts :name					# name
puts :name.class			# Symbol
# Symbol的方法数比String少得多
puts :name.methods.size		# 81
puts "Jane".methods.size	# 181
# 相同的Symbol具有相同的ID,字符串则不然
puts :name.object_id		# 72028
puts :name.object_id		# 72028
puts "Jane".object_id		# 60
puts "Jane".object_id		# 80数值类型(Numeric)
整数(Integer)
- 支持任意长度的整数(类似python);
 - 可以使用前导符号:
0开头表示8进制,0x开头表示16进制,0b开头表示2进制; - 数字中可以使用
_以增强可读性(类似verilog)。 
整数除法的结果也是整数。
浮点数(Float)
基于C语言的double类型实现的,精度有限。
123.4
1e6
1e-5# 两种方法打印格式化的小数
puts sprintf "%.4f" % (1/3.0)
printf "%.7f" % (5/3.0)如果要任意精度可以使用BigDecimal标准库。
require 'bigdecimal'
num = BigDecimal("1.123456789012345678901234567890")有理数(Rational)
puts Rational(3, 4)     # 3/4
puts 2/3r               # 2/3
puts 2.5.to_r           # 5/2复数(Complex)
puts Complex(1, 2)  # 1+2i
puts 1+2i           # 1+2i范围(Range)
表示连续值的范围。..包含结束值;...不包含结束值。
range1 = 1..5  	# 包含1到5
range2 = 1...5	# 包含1到4
puts range1.to_a字符串(String)
创建
website = "google.com"					# 字面值
website = String.new "zetcode.com"		# 对象引号与转义
- 双引号支持插值
#{},单引号不支持; - 双引号支持更多的转义字符(
\n等),单引号仅支持\\和\'。 
puts "Hello, #{name}"  	# Hello, Ruby
puts 'Hello, #{name}'  	# Hello, #{name}
puts "Hello\nWorld!"  	# 有换行
puts 'Hello\nWorld!'  	# Hello\nWorld!访问元素
msg = "Ruby language"
# 测试字符串是否为msg的子串
puts msg["Ruby"]            # Ruby
puts msg["Python"]          # 无输出
# 索引
puts msg[0]                 # R
puts msg[-1]                # e
# 切片(前闭后开)
puts msg[0, 3]              # Rub
puts msg[0, msg.length]     # Ruby language
# 范围
puts msg[0..9]              # Ruby langu多行字符串
三种方法。第三种(Here Document语法)EOF可以是任意字符。
puts "I hear Mariachi static on my radio 
And the tubes they glow in the dark"
puts %/Carmelita hold me tighter
I think I'm sinking down/
puts <<EOF
Well, I'm sittin' here playing solitaire
With my pearl-handled deck
EOF变量插值
name = "Jane"
age = 17
puts "#{name} is #{age * 2} years old"
puts "%s is %d years old" % [name, age]连接
四种方式都可以。
puts "Ruby" + " programming" + " language"
puts "Ruby" " programming" " language"
puts "Ruby" << " programming" << " language"
puts "Ruby".concat(" programming").concat(" language")冻结
在 Ruby
中,默认情况下字符串是可变的,调用freeze使之不可变。
msg = "Jane is " 
msg << "17 years old"
puts msg
msg.freeze
msg << "and she is pretty"	# 报错比较
puts "aa" == "ab"
puts "Jane".eql? "Jane"# 左边小为-1,左边大为1,相等为0
puts "ab" <=> "ba"		# -1
puts "ab".casecmp "ba"	# -1常用方法
word = "Determination"
# 字符串长度
puts word.size                  # 13
# 是否为空
puts word.empty?                # false
# 测试是否含有子串
puts word.include? "tion"		# true
# 是否以指定字符串开头结尾
puts word.start_with? "De"      # true
puts word.end_with? "tion"      # true
# 大小写转换
puts word.upcase                # DETERMINATION
puts word.downcase              # determination
# 首字母大写
puts "word".capitalize          # Word
puts "aaBB".swapcase            # AAbb
# 清空
word.clear# 显示原始字符、去除末尾\n
msg = "Jane\t17\nThomas\t23\n"
puts msg.inspect			# "Jane\t17\nThomas\t23\n"
puts msg.chomp.inspect		# "Jane\t17\nThomas\t23"格式化
基本同python,更详细的自己查。
puts "%10d" % 16567			#      16567
puts "%s" % "zetcode"		# zetcode
puts "%.2f" % 123.12345		# 123.12数组(Array)
可以存储任意类型的对象。类似python列表。
arr = [1, "Ruby", :symbol]
arr.each do |it|
    puts it
end哈希(Hash)
键值对的集合。类似python字典。
domains = {
    :de => "Germany",
    :sk => "Slovakia",
    :us => "United States",
    :no => "Norway"
}
puts domains.keys
puts domains.values类型转换
p "12".to_i			# 转换为整数
p "13".to_f			# 转换为浮点数
p "12".to_r			# 转换为有理数
p "13".to_c			# 转换为复数
p 124.to_s			# 转换为字符串
p "Jane".to_sym		# 转换为符号
p domains.to_a		# 哈希转换为数组运算符
算术运算符
+、-、*、/、%、**。没有自增自减运算符。
逻辑运算符
&&、||、!。还有and、or、not,意思相同,只是优先级低于符号的。
关系运算符
<、<=、>、>=、!=、<=>(字符串中提到了)。==:比较两个对象的值是否相等;eql?:判断两个对象的值是否相等,且类型相同;===:判断一个对象是否符合某个模式或属于某个范围。
puts 5 == 5.0           # true
puts 5.eql? 5.0         # false
puts (1..10) === 5      # true(5在范围内)
puts String === "abc"   # true(abc是String的实例)位运算符
&、|、~、^、<<、>>。
复合赋值运算符
+=、-=、*=、**=、/=、%=、&=、|=……
三元运算符
cond ? exp1 : exp2。
成员访问运算符
::用于访问模块或类中的常量,也可以访问方法(很少用);.用于访问模块或类中的方法。
class MyMath
    Pi = 3.1415926535
    def fun
        puts Pi
    end
end
module People
    Name = "People"
    def self.fun    # 有self是模块方法
        puts Name
    end
end 
puts MyMath::Pi     # 3.1415926535
m = MyMath.new
m.fun               # 3.1415926535
m::fun              # 3.1415926535
puts People::Name   # People
People::fun         # People
People.fun          # People流程控制
if
中括号表示可以省略。
if num < 0 [then]
    puts "#{num} is negative"
elsif num == 0
   puts "#{num} is zero"
elsif num > 0
   puts "#{num} is positive"
endcase
domain = gets.chomp
case domain
    when "us"
        puts "United States"
    when "de" 
        puts "Germany"
    when "sk" 
        puts "Slovakia"
    else
        puts "Unknown"
endwhile
在条件为true时执行。
while i < 10  do
   i = i + 1
   sum = sum + i
end在条件为false时执行。
until i >= 10  do
   i = i + 1
   sum = sum + i
endfor
# 遍历范围
for i in 1..5
    puts "当前值是:#{i}"
end
puts i		# 5
# 遍历数组
arr = ["Ruby", "Python", "Java"]
for lang in arr
  puts "编程语言:#{lang}"
end
# 遍历哈希
hash = {name: "Ruby", version: 3.1, type: "语言"}
for key, value in hash
  puts "#{key} => #{value}"
endeach
each语句的迭代变量(如下面的num)在外部作用域不可见,for是可见的。
arr = [1, 2, 3]
arr.each do |num|
    puts num
end
arr.each { |num|
    puts num
}
puts num	# 报错loop
无限循环。
loop do
	puts "这是一个无限循环"
endbreak、next
同break和continue。
redo
重新执行当前迭代。注意避免死循环。
flag = 0
for i in 1..3
    puts i
    if i == 2 and flag ==0
        flag = 1
        next
    end
end
# 1 2 2 3数组
创建
# 用字面值创建
arr1 = [1, 2, 3]                # [1, 2, 3]
arr1 = [1, 2, [3, 4, [5, 6]]]	# 多维
# 创建空数组
arr2 = Array.new                # []
arr2.push 1						# [1]
# 创建指定长度的数组,默认填充nil,可以不写()
arr3 = Array.new 3              # [nil, nil, nil]
arr4 = Array.new(3, "a")        # ["a", "a", "a"]
# 使用代码块动态填充数组
arr5 = Array.new(3) {|i| i*i}   # [0, 1, 4]
# 使用%w创建,空格分隔,无需引号
arr6 = %w{apple banana cherry}	# ["apple", "banana", "cherry"]
# 用to_a转换为数组
arr7 = (1..3).to_a              # [1, 2, 3]
# 用Array转换为数组
arr8 = Array(1..3)              # [1, 2, 3]读取
arr = %w{a b c d e f g h}
puts arr.first				# a
puts arr.last				# h
puts arr.at(3)				# d
puts arr[0]					# a
puts arr[-1]				# h
puts arr[0, 3].inspect		# ["a", "b", "c"]
puts arr[2..6].inspect		# ["c", "d", "e", "f", "g"]
puts arr[2...6].inspect		# ["c", "d", "e", "f"]
puts arr.values_at(1, 3, 5).inspect		# ["b", "d", "f"]
puts arr.values_at(-1, -3).inspect		# ["h", "f"]遍历
puts arr.inspect		# 以字符串形式输出,一行
puts arr				# 每行一个元素,和下面的each效果相同
arr.each do |e|
    puts e
end
arr.each_with_index do |num, idx|	# 附带下标idx
    puts "value #{num} has index #{idx}"
end添加
arr = []
arr.insert(0, 'E', 'F', 'G')    # 从指定位置插入
arr.push 'H'                    # 追加
arr.push('I', 'J', 'K')         # 追加多个
arr << 'L' << 'M'               # 同push
arr.unshift('A', 'B', 'C')      # 加在前面
arr.insert(3, 'D')              # 从指定位置插入
puts arr.inspect
# ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"]删除
arr = %w{ a b c d e}
# 删除最后一个元素
arr.pop             # ["a", "b", "c", "d"]
# 删除第一个元素
arr.shift           # ["b", "c", "d"]
# 删除特定位置的元素
arr.delete_at(0)    # ["c", "d"]
# 删除特定元素
arr.delete('d')     # ["c"]
# 删除所有元素
arr.clear			# []常用方法
arr1 = %w{a b c}
arr2 = %w{d e f}
# 连接数组
arr = arr1 + arr2
arr = arr1.concat arr2
# 长度
puts arr.length
# 比较数组,复制数组,删除数组某个元素
puts arr.eql? arr.dup				# true
puts arr.eql? arr.dup.delete_at(0)	# false
# 判断是否为空
puts arr.empty?查找
numbers = [1, 2, 2, 2, 3, 4, 5, 8, 11]
# 从左往右查找,返回下标
puts numbers.index 2        # 1
# 从右往左查找,返回下标
puts numbers.rindex 2       # 3
# 是否包含某元素
puts numbers.include? 3     # true哈希
创建
# 字面值
domains = {
    "de" => "Germany",
    "sk" => "Slovakia",
    "hu" => "Hungary",
    "us" => "United States",
    "no" => "Norway"                       
}
# 创建空对象
names = Hash.new读取
stones = {
    1 => "garnet",
    2 => "topaz",
    3 => "opal",
    4 => "amethyst"
}
# 读取给定键的值
puts stones.fetch 1     # garnet
puts stones[2]          # topaz
# 获得多个值,返回数组
puts stones.values_at(1, 2, 3).inspect
# ["garnet", "topaz", "opal"]遍历
# 遍历键和值
stones.each {
    |k, v| puts "Key: #{k}, Value: #{v}"
}
# 遍历键
stones.each_key {
    |key| puts "#{key}"
}
# 遍历值
stones.each_value {
    |val| puts "#{val}"
}添加
# 添加键值对
names[:a] = "Jane"			# 方式一
names.store(:b, "Thomas")	# 方式二删除
names[1] = "Jane"
names[2] = "Thomas"
names[3] = "Robert"
names[4] = "Julia"
names[5] = "Rebecca"
# 通过键删除
names.delete 4
# {1=>"Jane", 2=>"Thomas", 3=>"Robert", 5=>"Rebecca"}
# 删除符合条件的,不改变原哈希
puts names.reject { |k, v| v =~ /R.*/ }
# {1=>"Jane", 2=>"Thomas"}
puts names
# {1=>"Jane", 2=>"Thomas", 3=>"Robert", 5=>"Rebecca"}
# 删除符合条件的,同时改变原哈希
puts names.delete_if { |k, v| k<=3 }	# {5=>"Rebecca"}
puts names								# {5=>"Rebecca"}常用方法
puts domains.length				# 长度
puts domains.size				# 同上
puts domains.keys.inspect		# 键的数组
puts domains.values.inspect		# 值的数组names2 = names1.dup         	# 复制
puts names1.eql? names2     	# 判断是否等于
puts names1.empty?          	# 判断是否为空
names1.clear                	# 删除所有项puts domains.include? :no		# 键是否存在
puts domains.key? :me			# 同上
puts domains.value? "Germany"	# 值是否存在names1 = {
    1 => "Jane"
}
names2 = {
    2 => "Thomas"
}
# 合并,不修改原对象
names = names1.merge names2
puts names      # {1=>"Jane", 2=>"Thomas"}
puts names1     # {1=>"Jane"}
# 合并,带!表示修改了原对象
names = names1.merge! names2
puts names      # {1=>"Jane", 2=>"Thomas"}
puts names1     # {1=>"Jane", 2=>"Thomas"}面向对象
对象的创建
class Being
end
b = Being.new
puts b		# 打印对象实际上是打印to_s方法的返回值构造函数
class Person
    # 构造函数
    def initialize name	# 参数加不加括号都行
        @name = name
    end
    def get_name	# 实例变量只能通过方法访问
        @name
    end
end
p1 = Person.new "Jane"
puts p1.get_name可以设置默认参数,必须按参数顺序赋值,模拟了构造函数重载的行为。
class Person
    def initialize(name="unknown", age=0)
        @name = name
        @age = age        
    end
    def to_s
        "Name: #{@name}, Age: #{@age}"
    end
end
p1 = Person.new
p2 = Person.new "Robert"
puts p1		# Name: unknown, Age: 0
puts p2		# Name: Robert, Age: 0方法与访问修饰符
调用方法:
puts p1.get_name			# 用点运算符
puts p1.send(:get_name)		# send方法,参数是方法名的符号在Ruby中,所有数据成员都是私有的,访问修饰符只能在方法上使用。
public:可被任意对象调用,方法默认是public的。protected:只能被类及其子类的对象调用。private:仅限类的内部使用。
class Some
    def initialize
        # 以下调用都可以
        method1
        self.method1
        method2
        self.method2
        method3
        self.method3
    end
    public		# 表示之后的部分是public
    def method1
        puts "public method1 called"
    end
    private		# 表示之后的部分是private
    def method2
        puts "private method2 called"  
    end
    # private :method2	# 也可以直接指定某函数是private
    protected	# 表示之后的部分是protected
    def method3
        puts "protected method3 called"
    end          
end
s = Some.new
puts "-----------"
s.method1
# s.method2		# 报错
# s.method3		# 报错继承
Ruby中,数据成员和方法的可见性不受继承的影响,比如private方法会继承给子类(在Java中private无法继承)。
class Being
    def initialize
        puts "Being class created"
    end
end
# 使用小于符号表示继承
class Human < Being
   def initialize
       super	# 调用父类的构造
       puts "Human class created"
   end
end
Human.new
p Human.ancestors	# 打印祖先列表
# [Human, Being, Object, Kernel, BasicObject]super会调用父类中相同名称的方法。如果该方法没有参数,它将自动传递其所有参数;写super()才不会将任何参数传递给父方法。
class Base
    def show x=0, y=0
        p "x: #{x}, y: #{y}"
    end
end
class Derived < Base
    def show x, y
        super       # "x: 3, y: 3"
        super x     # "x: 3, y: 0"
        super x, y  # "x: 3, y: 3"
        super()     # "x: 0, y: 0"
    end
end
d = Derived.new
d.show 3, 3属性访问器
attr_reader:创建get方法;attr_writer:创建set方法,同时创建实例变量;attr_accessor:等于上面两个。
class Car
    attr_reader :name, :price
    attr_writer :name, :price  
    # attr_accessor :name, :price
end
c1 = Car.new
c1.name = "Porsche"
c1.price = 23500
puts "The #{c1.name} costs #{c1.price}"类常量
类常量不属于具体的对象,属于这个类。可以在类的内部直接访问类常量,就像是访问变量一样;在类的外部访问类常量需要用::。
class MMath
    PI = 3.141592
end
puts MMath::PI运算符重载
class Circle
    attr_accessor :radius
    def initialize r
        @radius = r
    end
    def +(other)
        Circle.new @radius + other.radius
    end
end
c1 = Circle.new 5
c2 = Circle.new 6
c3 = c1 + c2
puts c3.radius类方法
Ruby的方法可以分为类方法和实例方法。类方法只能在类上调用,不能在类的实例上调用,且类方法不能访问实例变量。
class Circle
    def initialize x
        @r = x
    end
    # 以self关键字开头的方法是类方法
    def self.info
        "This is a Circle class"
    end
    def area
        @r * @r * 3.141592
    end
end
p Circle.info
c = Circle.new 3
p c.area模块
Ruby
Module是方法、类和常量的集合,可以用于对相关的类进行分组。方法和常量可以放入单独的模块中。模块不能有实例。有点类似Java的包、C++的namespace,但不完全相同。
puts Math::PI
# include之后就可以直接使用
include Math
puts PI
puts sin 2# 另一个实例
module Device
    def switch_on ; puts "on" end    
    def switch_off ; puts "off" end
end
module Volume
    def volume_up ; puts "volume up" end    
    def vodule_down ; puts "volume down" end
end
module Pluggable
    def plug_in ; puts "plug in" end    
    def plug_out ; puts "plug out" end
end
class CellPhone
    include Device, Volume, Pluggable
    def ring
        puts "ringing"
    end    
end
cph = CellPhone.new
cph.switch_on		# on
cph.volume_up		# volume up
cph.ring			# ringing异常
异常是对象,是内置Exception类的后代。
捕获异常
begin
    a = 5 / 0
rescue => e
    p e		# #<ZeroDivisionError: divided by 0>
else		# 没有异常时执行
    puts "right"
ensure		# 无论如何都会执行
    puts "always excute"
endraise
age = 17
begin
    if age < 18
        raise "Person is a minor"
    end
    puts "Entry allowed"
rescue => e
    puts e		# Person is a minor
endcatch和throw
catch包围一个块,如果块中throw了对应的异常,则跳出该块。
# catch可以有返回值
result = catch(:exit_point) do
    value = 42
    throw(:exit_point, value)  # 将值 42 传递给 catch
    "This will never be executed"
end
puts result  # 42可以用于跳出深层循环:
catch(:done) do
    (1..10).each do |i|
        (1..10).each do |j|
            puts "i: #{i}, j: #{j}"
            throw(:done) if i * j > 30  # 跳出整个 catch 块
        end
    end
end
puts "Loop exited"