I am asked to write some code in Ruby that iterates over every n-th element of an array and prints it until all elements of the array are printed.
The question reads: Imagine an iterator that accesses an array in strides and runs some code at each stride. If the strides reach the end of the array then they simply begin anew from the array's beginning. For example:
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
Assume that the stride is equal or greater than 1, and that both the stride and the array's size are not a integral/whole multiple of each other, meaning that the whole array can be printed using a given stride. Fill in the code that's missing:
class Array
def stride(step)
numelems = ... # size of the array
...
end
end
It is obvious that numelemns = self.length()
. However am having trouble with the rest.
I am going to try writing some code in Python that accomplishes this task, but I am afraid that I will not be able to translate it to Ruby.
Any ideas? The answer should not be more than 4-5 lines long as the question is one that our proffessor gave us to solve in a couple of minutes.
A solution to this is provided below (thanks @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
CodePudding user response:
This almost does what I think you want but breaks if the step is array.length 1 array.length
(but you mention that we should assume the stride is not a multiply of the array length).
class Array
def exhaustive_stride(step)
(self * step).map.with_index do |element, index|
next element if index == 0
element if index % step == 0
end.compact
end
end
x.exhaustive_stride 1
#=> [0, 1, 2, 3, 4]
x.exhaustive_stride 2
#=> [0, 2, 4, 1, 3]
x.exhaustive_stride 8
#=> [0, 3, 1, 4, 2]
[].exhaustive_stride 2
#=> []
Using the example array, it breaks when the stride is 5.
[0,1,2,3,4].exhaustive_stride 5
#=> [0, 0, 0, 0, 0]
CodePudding user response:
def striding(arr, n)
sz = arr.size
raise ArgumentError,
"invalid as arr.size = #{arr.size} is a multiple of n = #{n}" if
(sz % n).zero? || (arr.size.even? && n.even)
covered = Array.new(sz, false)
nbr_uncovered = sz
i = -n
while nbr_uncovered > 0
j = (i = n) % sz
puts "S".rjust(j 1)
unless covered[j]
covered[j] = true
nbr_uncovered -= 1
end
display_covered(covered)
end
end
def display_covered(covered)
covered.each { |c| print c ? 'X' : 'O' }
puts
end
striding [1,2,3,4], 3
S
XOOO
S
XOOX
S
XOXX
S
XXXX
striding [1,2,3,4,5,6,7,8,9,1,2,3,4,5], 11
S
XOOOOOOOOOOOOO
S
XOOOOOOOOOOXOO
S
XOOOOOOOXOOXOO
S
XOOOOXOOXOOXOO
S
XOXOOXOOXOOXOO
S
XOXOOXOOXOOXOX
S
XOXOOXOOXOXXOX
S
XOXOOXOXXOXXOX
S
XOXOXXOXXOXXOX
S
XXXOXXOXXOXXOX
S
XXXOXXOXXOXXXX
S
XXXOXXOXXXXXXX
S
XXXOXXXXXXXXXX
S
XXXXXXXXXXXXXX
striding [1,2,3,4,5,6,7,8,9,1,2,3,4,5], 7
#=> ArgumentError: invalid as arr.size = 14 is a multiple of n
CodePudding user response:
Imagine an iterator that accesses an array in strides and runs some code at each stride. If the strides reach the end of the array then they simply begin anew from the array's beginning.
Based on this specification, stride
will always iterate forever, unless the array is empty. But that is not a problem, since we can easily take
only the amount of elements we need.
In fact, that is a good design: producing an infinite stream of values lets the consumer decide how many they need.
A simple solution could look like this:
module EnumerableWithStride
def stride(step = 1)
return enum_for(__callee__, step) unless block_given?
return self if length.zero?
enum = cycle
loop do
yield enum.next
(step - 1).times { enum.next }
end
end
end
Enumerable.include(EnumerableWithStride)
A couple of things to note here:
I chose to add the stride
method to Enumerable
instead of Array
. Enumerable
is Ruby's work horse for iteration and there is nothing in the stride
method that requires self
to be an Array
. Enumerable
is simply the better place for it.
Instead of directly monkey-patching Enumerable
, I put the method in a separate module
. That makes it easier to debug code for others. If they see a stride
method they don't recognize, and inspect the inheritance chain of the object, they will immediately see a module named EnumerableWithStride
in the inheritance chain and can make the reasonable assumption that the method is probably coming from here.
For an empty array, nothing happens:
[].stride(2, &method(:p))
#=> []
stride
just returns self
(just like each
does) and the block is never executed.
For a non-empty array, we get an infinite stream of values:
x.stride(&method(:p))
# 0
# 1
# 2
# 3
# 4
# 0
# 1
# …
x.stride(2, &method(:p))
# 0
# 2
# 4
# 1
# 3
# 0
# 2
# …
x.stride(8, &method(:p))
# 0
# 3
# 1
# 4
# 2
# 0
# 3
# …
The nice thing about this infinite stream of values is that we, as the consumer can freely choose how many elements we want. For example, if I want 10 elements, I simply take
10 elements:
x.stride(3).take(10)
#=> [0, 3, 1, 4, 2, 0, 3, 1, 4, 2]
This works because, like all well-behaved iterators, our stride
method returns an Enumerator
in case no block is supplied:
enum = x.stride(2)
#=> #<Enumerator: ...>
enum.next
#=> 0
enum.next
#=> 2
enum.next
#=> 4
enum.next
#=> 1
enum.next
#=> 3
enum.next
#=> 0
enum.next
#=> 2
So, if we want to implement the requirement "until all the elements of the array are printed":
I am asked to write some code in Ruby that iterates over every n-th element of an array and prints it until all elements of the array are printed.
We could implement that something like this:
x.stride.take(x.length).each(&method(:p))
x.stride(2).take(x.length).each(&method(:p))
x.stride(8).take(x.length).each(&method(:p))
This is a pretty simplistic implementation, though. Here, we simply print as many elements as there are elements in the original array.
We could implement a more sophisticated logic using Enumerable#take_while
that keeps track of which elements have been printed and which haven't, and only stops if all elements are printed. But we can easily prove that after x.length
iterations either all elements have been printed or there will never be all elements printed (if the stride size is an integral multiple of the array length or vice versa). So, this should be fine.