我被要求用Ruby编写一些代码,迭代数组的每第n个元素,并打印它,直到数组的所有元素都被打印出来。
问题如下:假设一个迭代器以步长访问数组,并在每个步长运行一些代码。如果步长到达数组的末尾,则它们只是从数组的开头重新开始。例如:
x = [0,1,2,3,4]
x.stride(1) do |elem|; puts elem; end # prints 0,1,2,3,4
x.stride(2) do |elem|; puts elem; end # prints 0,2,4,1,3
x.stride(8) do |elem|; puts elem; end # prints 0,3,1,4,2
[].stride(2) do |elem|; puts elem; end # does not print anything, but the code is correct
假设跨距等于或大于1,并且跨距和数组大小都不是彼此的整数倍,这意味着可以使用给定的跨距打印整个数组。
class Array
def stride(step)
numelems = ... # size of the array
...
end
end
很明显,numelemns = self.length()
.然而,我在其他方面遇到了麻烦。我打算尝试用Python编写一些代码来完成这项任务,但我担心我无法将其翻译成Ruby。
有什么想法吗?答案不应该超过4-5行,因为这个问题是我们的教授让我们在几分钟内解决的。
下面提供了一个解决方案(感谢@user3574603):
class Array
def stride(step)
yield self[0]
(self * step).map.with_index do |element, index|
next element if index == 0
yield element if index % step == 0
end
end
end
3条答案
按热度按时间wribegjk1#
这几乎达到了我认为您想要的效果,但如果步长为array.length +1
array.length
(但您提到我们应该假设步长不是数组长度的倍数),则会中断。使用示例数组,它在跨距为5时中断。
nxowjjhe2#
想象一个迭代器,它以步长访问一个数组,并在每个步长运行一些代码,如果步长到达数组的末尾,那么它们只是从数组的开头重新开始。
基于这个规范,
stride
将永远迭代下去,除非数组为空,但这不是问题,因为我们可以很容易地只使用take
所需的元素数量。事实上,这是一个"好"的设计:产生无限的价值流让消费者决定他们需要多少。
一个简单的解决方案可能如下所示:
这里有几点需要注意:
我选择将
stride
方法添加到Enumerable
而不是Array
。Enumerable
是Ruby用于迭代的工作马,stride
方法中没有要求self
为Array
的内容。Enumerable
只是更适合它。我没有直接修补
Enumerable
,而是将该方法放在一个单独的module
中,这使得其他人更容易调试代码。如果他们看到一个他们不认识的stride
方法,并检查对象的继承链,他们将立即在继承链中看到一个名为EnumerableWithStride
的模块,并且可以合理地假设该方法可能来自这里:对于空数组,什么也不会发生:
stride
只返回self
(就像each
一样),并且该块永远不会执行。对于一个非空数组,我们得到一个无限的值流:
这个无限的价值流的好处在于,作为消费者,我们可以自由选择我们想要的元素数量,例如,如果我想要10个元素,我只需
take
10个元素:这是因为,像所有行为良好的迭代器一样,如果没有提供块,我们的
stride
方法返回一个Enumerator
:因此,如果我们想实现"直到打印完数组的所有元素"的要求:
我被要求用Ruby编写一些代码,迭代数组的每第n个元素,并打印它,直到数组的所有元素都被打印出来。
我们可以像这样实现它:
这是一个相当简单的实现,在这里,我们只打印与原始数组中的元素一样多的元素。
我们可以使用
Enumerable#take_while
实现一个更复杂的逻辑,它跟踪哪些元素已经打印,哪些元素没有打印,只有当所有元素都打印完时才停止。但是我们可以很容易地证明,在x.length
迭代之后,要么所有元素都打印完,要么永远不会打印完所有元素(如果跨距大小是数组长度的整数倍,反之亦然)。所以,这应该没问题。1szpjjfi3#