Ruby代码迭代数组的每第n个元素并打印它,直到所有元素都被打印出来?

1wnzp6jl  于 2023-01-30  发布在  Ruby
关注(0)|答案(3)|浏览(189)

我被要求用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
wribegjk

wribegjk1#

这几乎达到了我认为您想要的效果,但如果步长为array.length +1array.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
#=> []

使用示例数组,它在跨距为5时中断。

[0,1,2,3,4].exhaustive_stride 5
#=> [0, 0, 0, 0, 0]
nxowjjhe

nxowjjhe2#

想象一个迭代器,它以步长访问一个数组,并在每个步长运行一些代码,如果步长到达数组的末尾,那么它们只是从数组的开头重新开始。
基于这个规范,stride将永远迭代下去,除非数组为空,但这不是问题,因为我们可以很容易地只使用take所需的元素数量。
事实上,这是一个"好"的设计:产生无限的价值流让消费者决定他们需要多少。
一个简单的解决方案可能如下所示:

module CoreExtensions
  module EnumerableExtensions
    module EnumerableWithStride
      def stride(step = 1)
        return enum_for(__callee__, step) unless block_given?

        enum = cycle

        loop do
          yield(enum.next)
          (step - 1).times { enum.next }
        end

        self
      end
    end
  end
end

Enumerable.include(CoreExtensions::EnumerableExtensions::EnumerableWithStride)

这里有几点需要注意:
我选择将stride方法添加到Enumerable而不是ArrayEnumerable是Ruby用于迭代的工作马,stride方法中没有要求selfArray的内容。Enumerable只是更适合它。
我没有直接修补Enumerable,而是将该方法放在一个单独的module中,这使得其他人更容易调试代码。如果他们看到一个他们不认识的stride方法,并检查对象的继承链,他们将立即在继承链中看到一个名为EnumerableWithStride的模块,并且可以合理地假设该方法可能来自这里:

[].stride
# Huh, what is this `stride` method? I have never seen it before.
# And it is not documented on https://ruby-doc.org/

# Let's investigate:
[].class.ancestors
#=> [
#     Array,
#     Enumerable,
#     CoreExtensions::EnumerableExtensions::EnumerableWithStride,
#     Object,
#     Kernel,
#     BasicObject
#   ]

# So, we're confused about a method named `stride` and we
# found a module whose name includes `Stride`.
# We can reasonably guess that somewhere in the system, 
# there must be a file named
# `core_extensions/enumerable_extensions/enumerable_with_stride.rb`.

# Or, we could ask the method directly:
meth = [].method(:stride)

meth.owner
#=> CoreExtensions::EnumerableExtensions::EnumerableWithStride

meth.source_location
#=> [
#     'core_extensions/enumerable_extensions/enumerable_with_stride.rb',
#     6
#   ]

对于空数组,什么也不会发生:

[].stride(2, &method(:p))
#=> []

stride只返回self(就像each一样),并且该块永远不会执行。
对于一个非空数组,我们得到一个无限的值流:

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
# …

这个无限的价值流的好处在于,作为消费者,我们可以自由选择我们想要的元素数量,例如,如果我想要10个元素,我只需take 10个元素:

x.stride(3).take(10)
#=> [0, 3, 1, 4, 2, 0, 3, 1, 4, 2]

这是因为,像所有行为良好的迭代器一样,如果没有提供块,我们的stride方法返回一个Enumerator

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

因此,如果我们想实现"直到打印完数组的所有元素"的要求:
我被要求用Ruby编写一些代码,迭代数组的每第n个元素,并打印它,直到数组的所有元素都被打印出来。
我们可以像这样实现它:

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))

这是一个相当简单的实现,在这里,我们只打印与原始数组中的元素一样多的元素。
我们可以使用Enumerable#take_while实现一个更复杂的逻辑,它跟踪哪些元素已经打印,哪些元素没有打印,只有当所有元素都打印完时才停止。但是我们可以很容易地证明,在x.length迭代之后,要么所有元素都打印完,要么永远不会打印完所有元素(如果跨距大小是数组长度的整数倍,反之亦然)。所以,这应该没问题。

1szpjjfi

1szpjjfi3#

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?
  raise ArgumentError,
    "invalid as arr.size and n = #{n} are both even numbers" if
    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

相关问题