我遇到了一个问题与猴子修补数组类。此任务需要满足8个规范。
我只给给予你的RSpecs和书面要求的一部分,我有麻烦w/,因为其他一切似乎是通过。
以下是Array Class Monkey Patch的书面要求:
- 编写一个新的
new_map
方法,在Array
类的示例上调用。它应该使用它所调用的数组作为隐式(self
)参数,但其他行为相同。(不完整) - 写一个new_select!方法,其行为类似于select,但会改变调用它的数组。它可以使用Ruby的内置集合select方法。(COMPLEX)
以下是需要满足的关于Array类的RSpecs:
注意:“返回一个更新值的数组”是唯一不通过的Spec。
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
我的代码:
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
3条答案
按热度按时间iibxawm41#
Ruby数组类:
block
就像一个方法,你可以在方法调用后指定一个块,例如:块被隐式地发送到方法,在方法内部,你可以用
yield
调用块。yield
->在方法调用后调用指定的块。在ruby中,yield
等价于yield()
,这在概念上等价于像这样调用块:block()
。yield(x)
->调用在方法调用后指定的块,并向其发送参数x,这在概念上等同于像这样调用块:block(x)
。下面是如何实现new_map():
这个注解有点高级,但实际上不必编写
self.each()
来调用new_map()中的each()方法。所有方法都是由某个对象调用的,即点左边的对象,称为接收器。例如,当你写:self是方法调用each()的接收者。
如果你不指定
receiver
,只写:.那么对于接收者,ruby使用当时分配给
self
变量的任何对象。在上面的new_map()中,ruby将把调用new_map()方法的Array赋值给self,所以each()将遍历该Array中的项。你必须对self变量小心一点,因为ruby经常会在不告诉你的情况下改变self变量的值。所以,你必须知道ruby在你代码中的任何一个特定点上给self变量赋值了什么--这需要经验。不过,如果你想知道ruby在代码中的某个特定点给self分配了什么对象,你可以简单地写:
如果有经验的rubyist看到你在new_map()中写
self.each {...}
,他们会感到惊讶,但在我看来,* 代码清晰性 * 胜过代码技巧,而且因为初学者在那里写self更有意义,所以继续这样做吧。当你有了更多的经验并想炫耀时,你可以在不需要的时候消除明确的接收者。这与显式返回的情况类似:隐式返回:
注意,你可以像这样写new_map():
将其与使用yield()的示例进行比较。当你使用yield()时,就好像ruby为你创建了一个名为
yield
的参数变量来捕获块。但是,对于yield,您使用不同的语法来调用块,即()
,或者如果块没有参数,则可以消除圆括号-就像调用方法时一样。另一方面,当您创建自己的参数变量来捕获块时,例如。def new_map(&my_block)
,你必须使用不同的语法来调用块:my_block.call(arg1, ...)
或:
myblock[arg1, ...]
请注意,#2就像调用方法的语法一样--只是用
[]
代替了()
。同样,有经验的rubyist将使用yield来调用块,而不是在参数变量中捕获块。但是,在某些情况下,您需要在参数变量中捕获块,例如。如果你想把块传递给另一个方法。
ijnw1ujt2#
看看这里的spec:
看起来他们只是想让你重写
Array.map
,这样它就可以处理任何给定的块。在你的实现中,你告诉方法以一种非常特定的方式工作,即对所有数组元素调用.to_s
。但是你不希望它总是将数组元素字符串化。您希望它对每个元素执行调用方法时提供的任何块。试试这个:请注意,在我的示例中,方法定义没有指定要对
self
的每个元素执行的任何特定操作。它只是循环遍历数组的每个元素,将元素(num
)返回到调用.new_map
时传递的任何块,并将结果铲入new_array
变量。有了
.new_map
的实现和给定的array = [1,2,3,4]
,你可以调用array.new_map(&:to_s)
(块是指定对数组中每个元素执行的任意操作的地方)并得到["1","2","3","4"]
,或者你可以调用array.new_map { |e| e + 2 }
并得到[3,4,5,6]
。wa7juj8i3#
我想对
new_select!
说几句话。选择vs选择!
您有:
你可以这样写
使用方法Array#select!你说可以使用Array#select,但没有提到
select!
。如果你不能使用select!
,你必须这样做:让我们试试看:
显式与隐式接收器
注意,我在这里没有为Array#replace和
Array#select
方法编写任何显式接收器。当没有明确的接收者时,Ruby假设它是self
,也就是a
。因此,Ruby将replace(select(&block))
视为使用显式接收器编写的:由您决定是否要包含
self.
。有些魔方可以,有些不行。您会注意到self.
并不包含在Ruby实现的Ruby内置方法中。还有一件事在某些情况下需要self.
以避免歧义。例如,如果taco=
是示例变量@taco
的setter,则必须编写self.taco = 7
来告诉Ruby您引用的是setter方法。如果你写taco = 7
,Ruby会假设你想创建一个局部变量taco
,并将其值设置为7。两种选择!
你的方法
new_select!
是select!
的直接替代品吗?也就是说,这两种方法在功能上是等价的吗?如果您查看Array#select!
的文档,您将看到它有两种形式,一种是您模仿的,另一种是您尚未实现的。如果
select!
没有被给定一个块,它返回一个枚举数。你为什么要这么做?假设你想写:select!
是否已被阻止?否,因此它必须返回一个枚举数,该枚举数将成为方法Enumerator#with_index的接收者。让我们试试看:
现在:
您可以将
enum1
视为“复合枚举器”。仔细查看Ruby在定义enum1
时返回的对象的描述。我们可以通过将枚举数转换为数组来查看枚举数的元素:
这五个元素中的每一个都被传递给块,并通过枚举器#each(调用Array#each)分配给块变量。
够了,但重点是,当没有给出块时,让方法返回枚举数是允许我们链接方法的。
你没有一个测试来确保
new_select!
在没有给出块的情况下返回一个枚举数,所以也许这不是预期的,但是为什么不试试呢?试试看: