Ruby(猴子修补阵列)

Jef*_*key 2 ruby arrays rspec ruby-on-rails

返回获取更多有关Bloc课程的帮助.决定带给你们一些我正在使用Monkey Patching the Array Class的问题.这个任务有8个规格 可以满足,我现在卡在一个左边,我不知道该怎么办.

我只会给你RSpecs和我遇到麻烦的部分的书面要求,因为其他一切似乎都在传递.此外,我将包括他们开始给我的启动脚手架,因此代码中没有混淆或无用的添加.


以下是Array Class Monkey Patch的书面要求:

  • 编写一个new_mapArray类的实例上调用的新方法.它应该使用它作为implicit(self)参数调用的数组,但在其他方面表现相同.(未完成)

  • 写一个new_select!行为与select类似的方法,但会改变调用它的数组.它可以使用Ruby的内置集合选择方法.(完整)


以下是有关Array类需要满足的RSpec:

注意:"返回具有更新值的数组"是唯一未传递的规范.

describe Array do
  describe '#new_map' do
    it "returns an array with updated values" do
      array = [1,2,3,4]
      expect( array.new_map(&:to_s) ).to eq( %w{1 2 3 4} )
      expect( array.new_map{ |e| e + 2 } ).to eq( [3, 4, 5, 6] )
    end

    it "does not call #map" do
      array = [1,2,3,4]
      array.stub(:map) { '' }
      expect( array.new_map(&:to_s) ).to eq( %w{1 2 3 4} )
    end

    it "does not change the original array" do
      array = [1,2,3,4]
      expect( array.new_map(&:to_s) ).to eq( %w{1 2 3 4} )
      expect( array ).to eq([1,2,3,4])
    end
  end

  describe '#new_select!' do
    it "selects according to the block instructions" do
      expect( [1,2,3,4].new_select!{ |e| e > 2 } ).to eq( [3,4] )
      expect( [1,2,3,4].new_select!{ |e| e < 2 } ).to eq( [1] )
    end

    it "mutates the original collection" do
      array = [1,2,3,4]
      array.new_select!(&:even?)
      expect(array).to eq([2,4])
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

这是他们开始使用的脚手架:

class Array
  def new_map
  end

  def new_select!(&block)
  end
end
Run Code Online (Sandbox Code Playgroud)

最后,这是我的代码:

class Array
  def new_map 
    new_array = []
    self.each do |num|
      new_array << num.to_s
    end
    new_array
  end

  def new_select!(&block)
    self.select!(&block)
  end
end
Run Code Online (Sandbox Code Playgroud)

7st*_*tud 5

Ruby数组类:

map { |item| block } ? new_ary
Run Code Online (Sandbox Code Playgroud)

A block类似于方法,您在方法调用后指定一个块,例如:

[1, 2, 3].map() {|x| x*2} #<---block
           ^
           |
       method call(usually written without the trailing parentheses)
Run Code Online (Sandbox Code Playgroud)

该块被隐式发送到该方法,并且在该方法内部可以使用该块调用该块yield.

yield - >调用方法调用后指定的块.在ruby中,yield相当于yield(),这在概念上等同于调用这样的块:block().

yield(x)- >调用方法调用后指定的块,向它发送参数x,这在概念上等同于调用块,如下所示:block(x).

那么,这是你如何实现new_map():

class Array
  def new_map
    result = []

    each do |item|
      result << yield(item)
    end

    result

  end
end

arr = [1, 2, 3].new_map {|x| x*2}
p arr

--output:--
[2, 4, 6]
Run Code Online (Sandbox Code Playgroud)

这个注释有点高级,但实际上你不必编写self.each()来调用new_map()中的each()方法.所有方法都由某个对象调用,即点左侧的对象,称为接收器.例如,当你写:

self.each {....}
Run Code Online (Sandbox Code Playgroud)

self是方法调用each()的接收者.

如果你没有指定receiver并且只是写:

each {....}
Run Code Online (Sandbox Code Playgroud)

...然后对于接收器,ruby使用当时分配给self变量的任何对象.在上面的new_map()中,ruby会将调用new_map()方法的Array分配给self,因此each()将遍历该Array中的项目.

你必须对自变量有点小心,因为ruby不断地改变自变量的值而不告诉你.因此,您必须知道 ruby在代码中的任何特定点为自变量赋予了什么 - 这是经验所带来的.虽然,如果您想知道ruby在代码中的某个特定点为自己分配了什么对象,您可以简单地写:

puts self
Run Code Online (Sandbox Code Playgroud)

经验丰富的rubyists如果看到你self.each {...}在new_map()中写道,就会喋喋不休地说他们的舌头,但在我看来代码清晰度胜过代码的诡计,因为初学者在那里写自我更有意义,继续做下去.当您获得更多经验并想要炫耀时,您可以在不需要时消除显式接收器.这与具有明确回报的情况类似:

def some_method
    ...
    return result
end
Run Code Online (Sandbox Code Playgroud)

和隐含的回报:

def some_method
    ...
    result
end
Run Code Online (Sandbox Code Playgroud)

请注意,您可以像这样编写new_map():

class Array
  def new_map(&my_block)  #capture the block in a variable
    result = []

    each do |item|
      result << my_block.call(item) #call the block
    end

    result

  end
end

arr = [1, 2, 3].new_map {|x| x*2}
p arr

--output:--
[2, 4, 6]
Run Code Online (Sandbox Code Playgroud)

将其与使用yield()的示例进行比较.当你使用yield()时,就好像ruby创建了一个yield为你命名的参数变量来捕获块.但是,使用yield会使用不同的语法来调用块,即(),如果它们不是块的参数,则可以删除括号 - 就像调用方法时一样.另一方面,当您创建自己的参数变量来捕获块时,例如def new_map(&my_block),您必须使用不同的语法来调用块:

  1. my_block.call(arg1, ...)

要么:

  1. myblock[arg1, ...]

请注意,#2就像调用方法的语法 - 除了替换[]代替().

有经验的rubyists将再次使用yield来调用块而不是在参数变量中捕获块.但是,在某些情况下,您需要在参数变量中捕获块,例如,如果要将块传递给另一个方法.