Proc类

Proc类是什么?

使块对象化的类

hello1 = Proc.new do |name|
    puts "hello, #{name}"
end

hello2 = proc do |name|
    puts "hello, #{name}"
end

hello1.call("Aaron")
hello2["Ruby"]
double = Proc.new do |*args|
    args.map{|i| i * 2}
end

p double.call(1, 2, 3)
p double[1, 2, 3]

lambda

与proc的区别是,调用call方法时,参数数量必须与快的一致。而proc不要求。

prc1 = Proc.new do |a, b, c|
    p [a, b, c]
end

prc1.call(1, 2)
#=> [1, 2, nil]


prc2 = lambda do |a, b, c|
    p [a, b, c]
end

proc2.call(1, 2)
#=> 报错 ArgumentError

另一个区别,lambda可以使用return将值从块中返回。

def power_of(n)
  lambda do |x|
    #注意,返回的是Proc对象,而不是数值。
    return x ** n
  end
end

cube = power_of(3)
p cube.call(5)
#=> 125

#使用proc
def power_of(n)
  Proc.new do |x|
    return x ** n
  end
end

cube = power_of(3)
p cube.call(5)
#=> 报错 LocalJumpError

另一种写法 ->

square = ->(n){ return n ** 2}
p square[5]
#=> 25

通过Proc参数接收块

def total2(from, to, &block)
  result = 0
  from.upto(to) do |num|
    if block
      result += block.call(num)
    else
      result += num
    end
  end

  return result
end

p total2(1, 10)
p total2(1, 10) {|num| num ** 2}

to_proc方法

#对符号:to_i使用Symbol#to_proc方法,会生成如下Proc对象
Proc.new{|arg| arg.to_i}

to_class = :class.to_proc
#=> Proc.new{|arg| arg.class}

p to_class.call("test")     #=> String
p to_class.call(123)        #=> Fixnum
p to_class.call( ** 1000)   #=> Bignum
%w(42 39 56).map{|i| i.to_i }

%w(42 39 56).map{&:to_i }

[Integer, String, Array, Hash, File, IO].sort_by(&:name)

Proc的特征

#这就是传说中的闭包了。将处理内容、变量等环境同时进行保存的对象。
def counter
  c = 0
  Proc.new do
    c += 1
  end
end

c1 = counter
p c1.call   #=> 1
p c1.call   #=> 2
p c1.call   #=> 3

c2 = counter
p c2.call   #=> 1
p c2.call   #=> 2

p c1.call   #=> 4

Proc类的实例方法

prc.call(args, ...) prc[args, ...] prc.yield(args, ...) prc.(args, ...) prc===arg

调用方法

prc = Proc.new{|a, b| a + b}

p prc.call(1, 2)
p prc[3, 4]
p prc.yield(5, 6)
p prc.(7, 8)
p prc === [9, 10]

prc.arity

返回作为call方法参数的块变量个数

proc0 = Proc.new{ nil }             #=> 0
proc1 = Proc.new{ |a| a }           #=> 1
proc2 = Proc.new{ |a, b| a + b}     #=> 2
proc4 = Proc.new{ |*args| args }    #=> -1

prc.parameters

返回关于块变量的详细信息。返回值为[种类, 变量名]形式的数组

符号 意义
:opt 可省略的变量
:req 必需的变量
:rest 以*args形式表示的变量
:key 关键字参数形式的变量
:keyrest 以**args形式表示的变量
:block
proc0 = proc{ nil }
proc1 = proc{ |a| a }
proc2 = lambda{ |a, b| [a, b]}
proc3 = lambda{ |a, b=1, *c| [a, b, c] }
proc4 = lambda{ |a, &block| [a, block] }
proc5 = lambda{ |a:1, **b| [a, b] }

p proc0.parameters      #=> []
p proc1.parameters      #=> [[:opt, :a]]
p proc2.parameters      #=> [[:req, :a], [:req, :b]]
p proc3.parameters      #=> [[:req, :a], [:opt, :b], [:rest, :c]]
p proc4.parameters      #=> [[:req, :a], [:block, :block]]
p proc5.parameters      #=> [[:key, :a], [:keyrest, :b]]

prc.lambda?

判断prc是否通过lambda定义的方法

prc1 = Proc.new{|a, b| a + b}
p prc1.lambda?
#=> false

prc2 = lambda{|a, b| a + b}
p prc2.lambda?
#=> true

prc.source_location

返回定义prc程序代码的位置。返回值为[代码文件名, 行编号]

prc0 = Proc.new { nil }
prc1 = Proc.new{ |a| a }

p prc0.source_location      #=> ["/Users/Aaron/Desktop/learn.rb", 1]
p prc1.source_location      #=> ["/Users/Aaron/Desktop/learn.rb", 2]