使用while循环构建Ruby数组的最简洁方法是什么?

时间:2017-11-07 16:09:52

标签: arrays ruby

Ruby有很多很好的迭代方法,可以直接返回结果。这主要涉及数组方法。例如:

def ten_times_tables
  (1..5).map { |i| i * 10 }
end

ten_times_tables # => [10, 20, 30, 40, 50]

但是,我有时想使用while进行迭代并直接返回结果数组。例如,数组的内容可能取决于预期的最终值或某些累加器,甚至取决于我们无法控制的条件。

(设计)示例可能如下所示:

def fibonacci_up_to(max_number)
  sequence = [1, 1]

  while sequence.last < max_number
    sequence << sequence[-2..-1].reduce(:+)
  end

  sequence
end

fibonacci_up_to(5) # => [1, 1, 2, 3, 5]

对我来说,这种方法感觉非常“非Ruby”#34;我构造,命名,然后返回一个数组的事实感觉就像一个反模式。到目前为止,我能想到的最好的是使用tap,但它仍然感觉非常icky(并且非常嵌套):

def fibonacci_up_to(max_number)
  [1, 1].tap do |sequence|
    while sequence.last < max_number
      sequence << sequence[-2..-1].reduce(:+)
    end
  end
end

有没有其他人对此类问题有任何更聪明的解决方案?

2 个答案:

答案 0 :(得分:5)

你可能想要在这种情况下寻找的东西(虽然你的人为设计的例子比你的实际用例更适合这种情况)正在创建一个Enumerator,所以你设想的例子变成:

来自初始化的文档:

fib = Enumerator.new do |y|
  a = b = 1
  loop do
    y << a
    a, b = b, a + b
  end
end

然后调用它:

p fib.take_while { |elem| elem <= 5 }
#=> [1, 1, 2, 3, 5]

所以,你创建一个枚举器来迭代你的所有值,然后一旦你有了它,你可以迭代它并以任何常用的Ruby-ish方式收集你想要的数组值

答案 1 :(得分:0)

与Simple Lime的Enumerator解决方案类似,您可以编写一个在Enumerator中包装自身的方法:

def fibonacci_up_to(max_number)
  return enum_for(__callee__, max_number) unless block_given?

  a = b = 1
  while a <= max_number
    yield a
    a, b = b, a + b
  end
end

fibonacci_up_to(5).to_a # => [1, 1, 2, 3, 5]

这与从方法返回Enumerator实例的结果相同,但它看起来更好,您可以使用yield关键字而不是yielder块变量。它还可以让你做一些整洁的事情:

fibonacci_up_to(5) do |i|
  # ..
end