1.方法声明
def 方法名称(参数名称, 默认值参数名称 = 值, *数组参数名称, &代码块参数名称)
#方法体
end
2.方法分类
2.1类方法
接收者不是对象而是类本身的方法。
class 类名
def self.类方法名称
end
end
2.2实例方法
以对象为接收者的方法。
class 类名
def 实例方法名称
end
end
2.3函数式方法
没有接收者的方法。
class HelloWorld
def initialize(myname = "Ruby")
@name = myname
end
def hello
#puts为函数式方法
puts "Hello,world.I am #{@name}."
end
bob = HelloWorld.new("Bob")
bob.hello
3.参数分类
名称 | 修饰符 |
值参数 | —————— |
默认值参数 | —————— |
数组参数 | 用 * 修饰 |
代码块参数 | 用 & 修饰 |
4.参数传递方式
方法的参数顺序必须依次为:形参,有默认值的形参,*数组形参(任意数量的参数),&代码块形参(代码块作为对象)。
实参的数量需与形参的数量相同,虽然有时候看似没赋予实参,实际上是提供了null
值或类型的默认值或其它值,总之必须有值。
实参的类型需与形参的类型兼容,所谓兼容指的是比如存在继承关系。
实参的传递不仅支持按位置从左往右依次传递方式,还支持按命名参数传递方式。
//按命名参数传递
方法(参数名称1: 实参值1, 参数名称2: 实参值2)
5.带符号的方法
方法后的符号不可省略,因为有时候同一个类会有两个名字一样的方法
5.1方法名=
比如一个对象o有一个叫做x=的方法,下面两种写法所做的事情是一样的
o.x=(1) #常规写法
o.x = 1 #复制写法
5.2方法名?
以问号结尾的方法返回布尔值
empty?
5.3方法名!
告诉开发者使用此方法时要多加小心
sort!
6.递归方法
在方法代码体中调用方法自身,这种方法被成为递归方法。
def factorial(n)
if n < 1
raise "argument must be > 0"
elsif n == 1
1
else
n*factorial(n-1)
end
end
7.单例(Singleton)方法
单例方法是单一对象方法。只在定义过的对象上可用。
class Car
def inspect
"Cheap car"
end
end
porsche = Car.new
porsche.inspect # => Cheap car
def porsche.inspect
"Expensive car"
end
porsche.inspect # => Expensive car
# 其他对象不受影响
other_car = Car.new
other_car.inspect # => Cheap car
8.Missing方法
针对不存在的方法,Ruby 同样会进行处理。它会将这个不存在的方法名作为参数传递给 method_missing
。method_missing
默认会抛出一个 NameError 异常,可以根据你的应用场景重新定义这个方法,许多库都是这么做的。看下面这个例子:
# id 是被调用的方法,* 号语法将所有参数收集到 "arguments" 数组内
def method_missing(id, *arguments)
puts "调用了不存在的方法:#{id}。它的参数:#{arguments.join(", ")}"
end
__ :a, :b, 10
# => 调用了不存在的方法:__。它的参数:a, b, 10
上面的代码会打印出调用细节,你也可以选择任何适当的方式处理这个消息。
9.无名函数(Nameless Function)之块(Block)
块(Block) 又称为 代码块,块的发明灵感来源于函数式编程中的高阶函数(Higher Order Function):如果一个函数可以接受一个或多个函数作为参数,或者返回值也是函数,满足其中一个,这个函数就被称为高阶函数,这种编程风格就被称为高阶函数风格(Higher Order Function Style)。
块是无名函数(Nameless Function), 您可以将块作为参数传递给高阶函数,然后该高阶函数可以调用传入的块。
许多其它语言都采用这种编程风格,在其它语言中,声明高阶函数的方式是通过形参来规定传入的参数为函数。而在 Ruby 中,高阶函数的语法有所不同,在方法内部,您可以使用带有值或不带值的yield
关键字表明此为高阶函数。
在C语言中,for
循环的作用就是将符合条件的值不断循环传递给后面的语句块处理,但是,如果不同的筛选条件,会让我们每次需要重新设置条件,于是,Ruby语言把不同的条件封装成不同的迭代器方法以供编程过程中很方便地直接调用,但是如何将方法内的值传递给语句块处理呢?于是,此时Ruby语言借鉴了函数式编程中的高阶函数风格,将方法内的值传递给传递进来的无名函数,即解决了此问题。所以,块这种无名函数,早期是为了实现不同的循环方法而设计的,但后来从循环延伸到任何需要这种高阶函数风格的场景。
for ( init; condition; increment )
{
//语句;
}
程序的控制流首先执行方法,当遇到yield时,控制流会从方法转移到那个与方法调用相关联的代码块中,当程序执行完代码块之后,方法重新获得控制权并且从位于yield之后的第一个语句开始继续执行。如果想给代码块传递实参,可以在yield后面放上一个由逗号分隔的表达式列表。
9.1带块的方法的声明
方法名内的形参和yield内的实参都是可选的。
def 方法名(方法形参)
.
.
yield (块实参)
.
.
end
9.2带块的方法的调用
,位于do~end
(包括do
和end
关键字)或者花括弧{~}
(包括花括弧)中间的代码就是 代码块 。代码块不可单独存在,只有出现在一个方法调用之后才是合法的。
块变量的作用域在块内部使用(块局部变量)。
#单行语句
对象.方法名(方法实参,···) { |块形参名称1;块局部变量名称1,...| 块内容 }
#多行语句
对象.方法名(方法实参,···) do |块形参名称1;块局部变量名称1,...|
块内容
end
9.3作用域
首先回顾一下:方法内的变量被称作局部变量,既然是局部变量,那它的作用域就是方法内,可被方法内的其他成员使用,在方法外不可用。
我们可以把外面的方法暂且称作外围方法,代码块暂且称作嵌套方法,根据局部变量的作用域,外围方法内的方法形参和方法局部变量自然而然可以被该方法内的所有代码块直接使用。代码块既然是嵌套方法,根据局部变量的作用域,只可以在块内部使用,块外部无法访问块级局部变量。
所以,如果一个代码块对一个已经在它外部定义过的变量进行赋值,那么不会创建一个新的块级局部变量,而是将新值赋值给那个已经存在的方法局部变量。
有时候,虽然给我们带来了便利,但是有些时候,我们并不想在代码块内修改外围的方法局部变量,只需要在块形参后面加上分号,分号后面接块级局部变量。
x = y = 0
1.upto(4) do |x;y|
y = x + 1
puts y*y
end #4,9,16,25
[x,y] #[0,0]
9.4示例
不带yield实参
def myloop
while true
yield
end
end
num = 1
myloop do
puts "#{num}"
break if num > 10
num *= 2
end
#1,2,4,8
带yield实参
def sequence(n,m,c)
i = 0
while(i < n)
yield m*i+c
i += 1
end
end
sequence(3,5,1) { |y| puts y } #1,6,11
10.无名函数(Nameless Function)之闭包(Closure)
形成闭包的两个特征:
- 块需要调用外围方法的参数或局部变量
- 外围方法有了返回以后,外围方法的参数或局部变量没有立即被销毁,依然存在于内存中,直至块执行完毕
首先回顾一下:代码块可以使用在其外定义的方法参数和方法局部变量。比如
def multiply(data, n)
data.collect { |x| x*n }
end
puts multiply([1,2,3], 2) #2,4,6
一旦multiply方法有了返回以后,n方法参数将会被销毁,所以,此时还没有形成闭包。那如何形成闭包呢?
可以把代码块变成一个proc或一个lambda,让multiplier方法返回后仍可以访问n,这样便形成了闭包。
def multiplier(n)
lambda { |data| data.collect { |x| x*n } }
end
doubler = multiplier(2) #返回一个lambda对象
puts doubler.call([1,2,3]) #2,4,6
代码块是Ruby的一种句法结构,它们不是对象,也不能像对象一样操作,不过,可以创建对象来表示一个代码块。根据对象的创建方式,它被称作一个proc或一个lambda。proc的行为与代码块类似,而lambda的行为则与方法类似,不过它们都是Proc的实例。
10.1Proc方式
#隐性传递Proc对象
def sequence(n,m,c,&b)
i = 0
while(i < n)
b.call(i*m + c)
i += 1
end
end
sequence(5,2,2) { |x| puts x }
#显性传递Proc对象
def sequence(n,m,c,b)
i = 0
while(i < n)
b.call(i*m + c)
i += 1
end
end
p = Proc.new { |x| puts x }
sequence(5,2,2,p)
10.2Lambda方式
succ = lambda { |x| x+1 }
10.3Lambda字面量方式
succ = ->(x) { x+1 }
#无参数
-> { 1 + 1 }
11.迭代器(Iterator)
迭代器是一种特殊的带块的方法。
迭代器(iterator)是Ruby最为重要的特性之一,又称为 迭代器方法,虽然while、until和for循环是Ruby语言的核心部分之一,但是通常情况下我们更倾向于使用 迭代器 来编写循环。
从迭代器的英文拼写可以知道,迭代器表示的是循环(iterate)的容器(-or),所以迭代器是表示循环的方法。类似地,运算符(operator)也就是运算(operate)的容器(-or)。
11.1times迭代器
循环次数.times do
希望循环的处理
end
7.times do
puts "满地油菜花"
end
> ruby times.rb
满地油菜花
满地油菜花
满地油菜花
满地油菜花
满地油菜花
满地油菜花
满地油菜花
带块的times迭代器
循环的次数默认是从0开始计算的。
5.times do |i|
puts "第#{i}此的循环。"
end
> ruby times.rb
第0次的循环。
第1次的循环。
第2次的循环。
第3次的循环。
第4次的循环。
11.2each迭代器
对象.each do |块变量|
希望循环的处理
end
a = [3,2,1]
a[3] = a[2] - 1
a.each do |x|
print x+1
end #结果为"4321"
11.3loop迭代器
没有终止的循环,可以使用break跳出循环或者Ctrl+c来强行终止程序。
loop do
print "Ruby"
end
11.4upto/downto迭代器
n.times与0.upto(n-1)是等价的。
1.upto(6) { |x| print x } #prints "123456"
6.upto(1) { |x| print x } #prints "654321"
11.5map迭代器
a = [1,2,3,4]
b = a.map { |x| x*x } # [1,4,9,16]
11.6select/reject迭代器
a = [1,2,3,4]
b = a.select { |x| x%2==0 } # [2,4]
c = a.select { |x| x%2==0 } # [1,3]
11.7inject迭代器
a = [1,2,3,4]
a.inject do |sum,x|
sum + x
end # 计算元素的和:10
原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/ruby/rubylang/1545.html