"Hello World" ; puts("Hello World") ; "Hello World" ;...">Ruby基础 | CoolWind "Hello World" ; puts("Hello World") ; "Hello World" ;...">
跳至主要內容

Ruby基础

酷风大约 14 分钟

Ruby基础

命令行交互

  • 命令行体验学习参考open in new window

  • 命令行 输入 irb

    • => 命令的返回值
    • 直接输入 "Hello World"=> "Hello World"
    • puts("Hello World")
      • "Hello World"
      • => nil puts 命令永远返回 nil
    • 数学计算:3+2=> 5
      • 3**2 3的二次方
  • 执行 ruby 文件

    • ruby demo.rb

基础语法

  • Ruby 文件扩展名 .rb
  • 语句分隔符 分号(;)和 换行符(\n)
    • Ruby 代码在行尾遇到运算符,比如 +、- 或 反斜杠(/),它们表示一个语句的延续
  c = 1 +
    8
  puts(c)
=> 9  

Ruby 标识符

  • 变量、常量和方法的名称
    • 可以包含字母、数字和下划线字符(_)
    • 大小写敏感
    • 保留字 不能用作标识符

保留字

Here Document

* 是指 建立多行字符串
* 在 << 之后,可以指定一个字符串或标识符来终止字符串,且当前行之后直到终止符为止的所有行是字符串的值
* 如果终止符使用引号括起,引号的类型决定了面向行的字符串类型。

注释

  • 单行注释以 # 号开始
  • 多行注释以 =begin 开始,以 =end 结束,这之间的所有字符或者语句都会被 Ruby 解释器忽略
# 这是注释
# 这也是注释 puts "Hello Ruby!"
# 这也是注释 puts "Hello Ruby!"
# 这还是注释

def hello
    puts("Hello");
end

=begin
这是注释。 puts "Hello Ruby!"
这也是注释。puts "Hello Ruby!"
这也是注释。puts "Hello Ruby!"
这还是注释。puts "Hello Ruby!"
=end

编码

  • 中文,早期Ruby版本 ruby文件头,要加utf-8标识,否则报错:invalid multibyte char (US-ASCII)
    • **# -*- coding: UTF-8 -*-**
    • #coding=utf-8
  • 较新Ruby版本,无需添加
#!/usr/bin/ruby -w
# -*- coding: UTF-8 -*-
puts "你好,世界!";

数据类型

数值类型

  1. Number:数值类型
    1. 整型( Integer )
      1. 如果在 31 位以内( 4 字节 ),那为 Fixnum 实例
      2. 如果超过,即为 Bignum 实例
      3. 整数前使用一个可选的前导符号, 一个可选的基础指标,后跟一串数字
        1. 0 对应 octal 八进制
        2. 0x 对应 hex 十六进制
        3. 0b 对应 binary
      4. 下划线字符在数字字符串中被忽略
        1. 123 # Fixnum 十进制
        2. 1_234 # Fixnum 带有下划线的十进制
      5. 用问号标记的转义序列的整数值
        1. > ?\C-s #Control-s的值ASCII
        2. => "\u0013"
      6. ASCII 字符 的整数值
        1. "a".ord
    2. 浮点型:是带有小数的数字
      1. 浮点数是类 Float 的对象

字符串类型

  • 类 String 的对象
  • 使用 序列 #{ expr } 来表示字符串中的参数或表达式
    • puts "a + b = #{ a + b}"
    • a + b = 3

转义字符

范围类型

  • 表示一个区间
  • 通过设置一个开始值和一个结束值来表示
    • s..e 包含结束值
    • s...e 不包含结束值
    • Range.new
  • 范围 (1..5) 意味着它包含值 1, 2, 3, 4, 5

特殊值

  • true
  • false
  • nil

数据结构

数组

  • 通过 [ ] 中以逗号分隔定义,且支持 range 定义
    • 通过 [i] 索引访问
  array_a = [1,2,3]
  array_a[0] = 111
  puts "打印数组元素0 : #{ array_a[0] }"
  
  # 打印数组元素0 : 111

哈希表类型(Map)

  • Ruby 哈希是在大括号内放置一系列键/值对,键和值之间使用逗号和序列 => 分隔
    • 尾部的逗号会被忽略
  • 哈希类对象Hash
    • 任意创建方式
    • myhash = Hash.new
    • myhash = Hash.new( "month" )
    • myhash = Hash.new "month"
  colors = { "red" => 0xf00, "green" => 0x0f0, "blue" => 0x00f }
  puts colors["red"]

类对象方法

  • Ruby 是一种完美的面向对象编程语言

  • 面向对象编程语言的特性包括:数据封装、数据抽象、多态性、继承

  • 类可以看成是属性和函数的组合

  • 关键字 class

  • 关键字 end 终止一个类

  • Ruby 中类名必须以大写字母开头;


  • 定义类
class ClassName

end # 

  • 创建对象
    • new 方法
    • new 传递参数用于初始化类变量
      • 声明方法 initialize
class ClassName
    
    def initialize(id, name, addr) 

      @cust_id   = id
      @cust_name = name
      @cust_addr = addr

   end
   
   def aa
     puts "aa"
   end
    
end

obj1 = ClassName.new
obj1 = ClassName.new(2, "百度", "北京市海淀区")
obj1.aa # 调用方法

to_s 方法

  • 任何类都有一个 to_s 实例方法来返回对象的字符串表示形式
class Box
   # 构造器方法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 定义 to_s 方法
   def to_s
      "(w:#@width,h:#@height)"  # 对象的字符串格式
   end
end

# 创建对象
box = Box.new(6, 8)

# 自动调用 to_s 方法
puts "String representation of box is : #{box}"



String representation of box is : (w:6,h:8)

访问控制

  • Public 方法: Public 方法可被任意对象调用。 默认情况下,方法都是 public 的,除了 initialize 方法总是 private 的。
  • Private 方法: Private 方法不能从类外部访问或查看,只有类方法可以访问私有成员
  • Protected 方法: Protected 方法只能被类及其子类的对象调用。访问也只能在类及其子类内部进行

类的继承

  • 继承,是面向对象编程中最重要的概念之一。继承允许我们根据另一个类定义一个类,这样使得创建和维护应用程序变得更加容易。
  • Ruby 不支持多继承,但是 Ruby 支持 mixins mixin
  • class BigBox < Box

# 定义类
class Box
   # 构造器方法
   def initialize(w,h)
      @width, @height = w, h
   end
   # 实例方法
   def getArea
      @width * @height
   end
end

# 定义子类
class BigBox < Box

   # 添加一个新的实例方法
   def printArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end

# 创建对象
box = BigBox.new(6, 8)

# 输出面积
box.printArea()

方法重载

  • 想要改变已经在父类中定义的方法的行为 这时可以保持方法名称不变,重载方法的功能即可

allocate 创建对象

  • 可以调用 allocate 来创建一个未初始化的对象,这样就不调用对象构造器 initialize 的情况下创建对象,即,使用 new 方法创建对象

方法

  • 总是以 小写字母 开头

如果以大写字母作为方法名的开头,Ruby 可能会把它当作常量,从而导致不正确地解析调用

  • 函数调用: 函数如无需传参, 可以不使用括号 > h # h()

  • 方法返回值:

    • 默认返回最后一个表达式的值
    • 使用 return 语句显示返回
      • 显示返回一个或多个值 return 1,2,3
  • 可变数量的参数

    • (*names)
  • 方法标记

    • 方法定义在类的外部,默认标记为 private
    • 方法定义在类中的,默认标记为 public
    • 一般情况下,如果想要访问类的方法时,首先必须实例化类。然后,就可以通过对象访问类的任何成员
  • 不用实例化即可访问方法

    • def ClassName.method_name
    • 可直接访问,不用实例化 ClassName.method_name

alias 语句

  • 用于为方法或全局变量起别名
alias 别名 方法名
alias 别名 全局变量

undef 语句

  • 用于取消方法定义

方法范例

def method_name (var1=value1, var2=value2)
   expr..
end

def hello( *names )

    print "Hello "

    for name in names
        print name, " "
    end

    puts "\nnames length is :#{names.length}"
end

class HelloWorld

    def reading_charge
    end

    def HelloWorld.greet  
        puts "Hello www.twle.cn"
    end
end

HelloWorld.greet

变量常量

  • 局部变量
  • 实例变量 @
    • 未初始化的实例变量的的默认值为 nil
  • 类变量 @@
    • 不能跨类使用
    • 类变量被共享在整个继承链中
  • 全局变量 $
    • 未初始化的全局变量的默认值为 nil
  • 常量
    • 常量以大写字母开头
    • 常量不能定义在方法内
    • 引用一个未初始化的常量会产生错误
    • 对已经初始化的常量赋值会产生警告
  • 特殊变量
    • 有着局部变量的外观,但行为却像常量
      • self: 当前方法的接收器对象
      • true: 代表 true 的值
      • false: 代表 false 的值
      • nil: 代表 undefined 的值
      • FILE: 当前源文件的名称
      • LINE: 当前行在源文件中的编号

运算符

算术运算符

  • Ruby 支持基本的加减乘除和指数操作
    • 加减乘除的操作符是 +-*/
    • 指数操作符为 **
puts 1/5 # 0 整数与整数相除 结果返回整数

比较运算符

赋值运算符

并行赋值

  • 多个变量可以通过一行的 Ruby 代码进行初始化
    • a, b, c = 10, 20, 30
  • 并行赋值可以用来交换两个变量的值
    • a, b = b, c

位运算符

逻辑运算符

三元运算符

  • condition ? expression1 : expression2

序列运算符

  • 序列运算符用于创建一系列连续的值

defined? 运算符

  • defined? 是一个特殊的运算符,以方法调用的形式来判断传递的表达式是否已定义。
    • 它返回表达式的描述字符串,如果表达式未定义则返回 nil
3.2.2 :001 > var1 = 11
 => 11 

3.2.2 :002 > defined? var1
 => "local-variable" 
3.2.2 :003 > defined? var2
 => nil 
3.2.2 :004 > 


> defined? puts      # puts 是一个方法
 => "method" 

点运算符 "." 和双冒号运算符 "::"

  • 点号(.)运算符用来访问类或者模块或实例的方法
  • 双冒号(::)运算符用来访问类或模块的常量

运算符优先级

条件语句

if


age = 16

puts("age = #{age}")


if age > 18
   puts "青春年少奋斗期"
elsif age <= 18 and age > 0
   puts "花样年华一样的年纪"
else
   puts "age 的值有错误"
end

if 修饰符

  • if 修饰符表示如果 conditiona 为真,则执行 code
age = 16

print "age = #{age}\n" if age > 14

unless

  • unless 语句和 if 语句作用相反
    • 如果 conditional 为假,则执行 statement1
    • 如果 conditional 为真,则执行 else 子句中指定的 statement2
    • else 语句是可选的。
unless conditional [then] 
   statement1
[else                     
   statement2 ]
end

unless 修饰符

  • 如果 conditional 为假,则执行 code
age = 16

puts("age = #{age} ")

puts("#{age} 岁是花样年华一样的年纪") unless age > 18

case

  • case 语句先对 expression1 进行匹配判断,然后根据匹配结果进行分支选择。
  • case 语句使用 === 运算符比较 when 指定的 expression ,若一致的话就执行 statement1
  • then 关键字通常可以省略
  • 当 case 的 "表达式" 部分被省略时,将计算第一个when条件部分为真的表达式
case expression1  
[when expression2 [, expression3 ...] [then]  
   statmente1 ]...
[else
   statement2 ]
end

# ---------
$age =  5

puts "age = #$age"

case $age
when 0 .. 2
    puts "婴儿"
when 3 .. 6
    puts "小孩"
when 7 .. 12
    puts "child"
when 13 .. 18
    puts "少年"
else
    puts "其他年龄段的"
end

# ---------
a = false
b = true
c = false
case
when a then puts 'a is true'
when b then puts 'b is true'
when c then puts 'c is true'
end

循环语句

while 语句

while conditional [do]  
   code1
end

或

while conditional [:]  
   code
end

# 语法中 do 或 : 可以省略不写

  i = 0
  while i < 5 do puts("在 while 循环语句中 i = #{i}" ); i +=1; end

  j = 0
  while j < 5
    puts("在 while 循环语句中 j = #{j}" )
    j +=1
  end

# 若要在一行内写出 while 式,则必须以 do 或 : 隔开条件式或程式区块

while 修饰符

  • 当 conditional 为真时,执行 statement
  • 如果 while 修饰符跟在一个没有 rescue 或 ensure 子句的 begin 语句后面, statement 会在 condition 判断之前执行一次。
statement while condition  
或

begin 
  statement  
end while condition  

until 语句

  • 当 conditiona 为假时,执行 statement
    • 语法中 do 可以省略不写。但若要在一行内写出 until 式,则必须以 do 隔开条件式或程式区块
until conditiona [do]  
   statement
end

until 修饰符

  • 当 condition 为 false 时,执行 statement
  • 如果 until 修饰符跟在一个没有 rescue 或 ensure 子句的 begin 语句后面, statement 会在 condition 判断之前执行一次
statement until condition
或

begin
   statement
end until condition

for和each

for variable [, variable ...] in expression [do]
   statement
end

 for step in 6..10   
    puts "step =  #{step}"
 end
(expression).each do |variable[, variable...]| code end

(6..10).each do |step|
    puts "step = #{step}"
 end

break 语句

  • 终止循环

next 语句

  • next 语句用于跳到循环的下一个迭代。

redo 语句

  • 重新开始循环

retry 语句

1.9 以及之后的版本不支持在循环中使用 retry 语句

  • retry 出现在迭代内、块内或者 for 表达式的主体内,则重新开始迭代调用。 迭代的参数会重新评估。
for i in 1..5
   retry if some_condition # 重新从 i == 1 开始
end
  • retry 出现在 begin 表达式的 rescue 子句中,则从 begin 主体的开头重新开始。
begin
   do_something   # 抛出的异常
rescue
   statement1     # 处理错误
   retry          # 重新从 begin 开始
end

块(block)

  • 包含在大括号 {} 内

  • 比方法更简单的组合语句的方式

  • 语法格式

block_name {
   statement1
   statement2
   ..........
}

yield 语句

  • 块可以被 yield 语句来调用
    • yield 语句 可以传参数,获取参数 {|a, b| statement}
    • 也可直接调用块
  • 通常使用 yield 语句从与其具有相同名称的方法调用块
    • 方法与块名称相同 如下,hello方法调用 hello块
def hello
    puts "在 hello 方法内"
    yield 17                     
    puts "你又回到了 hello 方法内"
    yield 33
end

hello{ |i| puts "你在块(block #{i}) 内" }

# 结果
在 hello 方法内
你在块(block 17) 内
你又回到了 hello 方法内
你在块(block 33) 内

传递块

  • 如果方法的最后一个参数前带有 &,则可以向该方法传递一个块,且这个块可被赋给最后一个参数
  • 如果 * 和 & 同时出现在参数列表中,& 应放在后面
def hello(&block)
    block.call
end
hello { puts "Hello World!"}

Hello World!

BEGIN 和 END 块

  • 每个 Ruby 源文件可以声明当文件被加载时要运行的代码块(BEGIN 块),以及程序完成执行后要运行的代码块(END 块)
    • 一个程序可以包含多个 BEGIN 和 END 块。
    • BEGIN 块按照它们出现的顺序执行。
    • END 块按照它们出现的相反顺序执行。

BEGIN 语句

  • BEGIN 语句中的 code 会在程序运行之前被调用
    • 多个按顺序执行,从上往下 执行
BEGIN {
   code
}

END 语句

  • END 语句会在程序运行结束后被调用
    • 多个 从下往上 执行
END {
   code
}

模块 (Module)

  • 是一种把方法、类和常量组合在一起的方式。

  • 定义了一个命名空间,相当于一个沙盒,在里边的方法和常量不会与其它地方的方法常量冲突

  • 好处

    • 模块提供了一个 命名空间 和避免名字冲突
    • 模块实现了 mixin 装置
  • 与类相似

    • 模块不能实例化
    • 模块没有子类
    • 模块只能被另一个模块定义
  • 语法格式

    • 以大写字母开头
    • 定义一个方法时,可以指定在模块名称后跟着一个点号,点号后跟着方法名
module <ModuleName>   
   statement1
   statement2
   ...........
end

module Hello
   PI = 3.141592654
   def Hello.sin(x)
   # ..
   end

   def Hello.cos(x)
   # ..
   end
end

require 语句

  • 加载模块
  • require filename
  • 使用 $LOAD_PATH << '.' 让 Ruby 知道必须在当前目录中搜索被引用的文件。
    • 如果不想使用 $LOAD_PATH,那么您可以使用require_relative 来从一个相对目录引用文件
$LOAD_PATH << '.'    

require 'quick.rb'
require 'hello'

y = Quick.sin(Trig::PI/4)
wrongdoing = Hello.sin(Moral::VERY_BAD)

include 语句

  • 在类中嵌入模块
$LOAD_PATH << '.'
require "week"

class Decade
include Week
   def no_of_months
      puts Week::FIRST_DAY
   end
end

Mixins

  • Ruby 不直接支持多重继承,但是 Ruby 的模块(Module)有另一个神奇的功能。
  • 它消除了多重继承的需要,提供了一种名为 mixin 的机制
  • Ruby 语言没有真正实现多重继承机制,而是采用 mixin 机制作为替代品

将模块 include 到类定义中,模块中的方法就 mix 进了类中

模块 A 由方法 a1 和 a2 组成
模块 B 由方法 b1 和 b2 组成
类 Hello 包含了模块 A 和 B
类 Hello 可以访问所有四个方法,即 a1、a2、b1 和 b2


$LOAD_PATH << '.'

require "a"
require "b"

class Hello
include A
include B
   def s1
   end
end

# ------------------------------------------

require "hello"

samp= Hello.new
samp.a1
samp.a2
samp.b1
samp.b2
samp.s1
  • 数学模块 Math
    • sqrt平方根
> a = 3 ** 2
> b = 4 ** 2
> Math.sqrt(a+b)
=> 5.0

日期 & 时间(Date & Time)

  • 内建了 Time 类用来处理日期和时间相关的操作
  • Time 类是基于操作系统提供的系统日期和时间之上

注意: Time 类可能无法表示 1970 年之前或者 2038 年之后的日期

t1 = Time.new
puts "当前时间 : " + t1.inspect
t2 = Time.now
puts "当前时间 : " + t2.inspect

time = Time.new
puts time.year    # => 日期的年份
puts time.zone    # => "UTC":时区名称

格式化时间和日期

time = Time.new

puts time.to_s
puts time.strftime("%Y-%m-%d %H:%M:%S")

Time 算术运算

now = Time.now           # 当前时间
puts now

past = now - 10          # 10 秒之前。Time - number => Time
puts past

迭代器

each 迭代器

collection.each do |variable|
   code
end

collect 迭代器

  • collect 方法返回整个集合,不管它是数组或者是哈希

collection = collection.collect

ary = ["Ali","Tencent","Baidu","JD","DiDi"]

b = Array.new
b = ary.collect{ |x|x..x }
puts b

Ali..Ali
Tencent..Tencent
Baidu..Baidu
JD..JD
DiDi..DiDi
  • collect 方法不是数组间进行复制的正确方式。 有另一个称为 clone 的方法,用于复制一个数组到另一个数组
a = [1,3,5,7,11]
b = a.collect{|x| 13*x}
puts b