两道测试题

看到两道关于 proc 的测试题,挺有意思,做个记录。

测试题一

题目

补全以下代码,实现一个计数器的功能,每次调用 a 可以得到累加的数值。

def counter n
  # ...
end

a = counter(10)

答案

def counter n
  proc { n += 1 }
end

a = counter(10)
p a.call
# => 11
p a.call
# => 12

counter 方法内部定义了一个 block, a = counter(10) 其实是得到了一个 匿名函数 proc { n += 1 },且 变量 n 有初始值 n = 10。用 call 调用第一次时,返回值是 n = 10 + 1,调用第二次时,返回值是 n = 11 + 1,以此类推。

测试题二

题目

实现以下类 EnuTest 的功能

enu = EnuTest.new do |x|
  x << 1
  x << 3
  x << proc { 'hello' }
end
  
enu.next # => 1
enu.next # => 3
enu.next # => 'hello'
enu.next # => raise error 'EOF'

答案

class EnuTest
  def initialize &block
    @eb = EnuBlock.new
    yield @eb
  end
  
  def next
    @eb.next
  end
end

class EnuBlock
  def initialize
    @blocks = []
  end
  
  def << obj
    if obj.is_a? Proc
      @blocks << obj
    else
      @blocks << proc { obj }
    end
  end
    
  def next
    if @blocks.empty?
      raise 'EOF'
    else
      @blocks.shift.call
  end
end

从题目的要求来看,EnuTest 的实例应该是一个 Array ,而这个 Array 起码要支持 integerproc 这两种类型。答案的思路是,把这个 Array 的元素全部定义为 proc,每次调用 next 方法,就会运行一个 proc

为了将输入的元素转成 proc ,答案还重新定义了 << 方法,遇到非 proc 的变量,先其转成 proc

最后用的的 shift 方法会将 array 第一个元素删除,并同时返回被删的这个元素。

· Ruby