Подтвердить что ты не робот

Как я могу получить ленивый массив в Ruby?

Как я могу получить ленивый массив в Ruby?

В Haskell я могу говорить о [1..], который представляет собой бесконечный список, лениво генерируемый по мере необходимости. Я также могу делать такие вещи, как iterate (+2) 0, который применяет любую функцию, которую я даю ей, для создания ленивого списка. В этом случае это даст мне все четные числа.

Я уверен, что могу делать такие вещи в Ruby, но не могу понять, как это сделать.

4b9b3361

Ответ 1

С Ruby 1.9 вы можете использовать класс Enumerator. Это пример из документов:

  fib = Enumerator.new { |y|
    a = b = 1
    loop {
      y << a
      a, b = b, a + b
    }
  }

  p fib.take(10) #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Кроме того, это хороший трюк:

  Infinity = 1.0/0

  range = 5..Infinity
  p range.take(10) #=> [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

Это работает только для последовательных значений.

Ответ 2

Недавно Enumerable:: Lazy добавлен в ruby ​​trunk. Мы увидим это в рубине 2.0. В частности:

a = data.lazy.map(&:split).map(&:reverse)

не будет оцениваться немедленно.
Результатом является экземпляр Enumerable:: Lazy, который может быть более ленивым. Если вы хотите получить фактический результат - используйте #to_a, #take(n) (#take теперь тоже ленив, используйте #to_a или #force) и т.д.
Если вы хотите больше на эту тему и мой патч C - см. Мой пост в блоге Ruby 2.0 Enumerable:: Lazy

Ответ 3

Lazy range (натуральные числа):

Inf = 1.0/0.0
(1..Inf).take(3) #=> [1, 2, 3]

Lazy range (четные числа):

(0..Inf).step(2).take(5) #=> [0, 2, 4, 6, 8]

Примечание. Вы также можете расширить Enumerable с помощью некоторых методов, чтобы сделать работу с ленивыми диапазонами (и т.д.) более удобной:

module Enumerable
  def lazy_select
    Enumerator.new do |yielder|
      each do |obj|
        yielder.yield(obj) if yield(obj)
      end
    end
  end
end

# first 4 even numbers
(1..Inf).lazy_select { |v| v.even? }.take(4)

output:
[2, 4, 6, 8]

Подробнее здесь: http://banisterfiend.wordpress.com/2009/10/02/wtf-infinite-ranges-in-ruby/

Существуют также версии lazy_map и lazy_select для класса Enumerator, которые можно найти здесь: http://www.michaelharrison.ws/weblog/?p=163

Ответ 5

Как я уже сказал в своих комментариях, реализация такой штуки, как ленивые массивы, не была бы разумной.

Использование Enumerable может работать хорошо в некоторых ситуациях, но в некоторых случаях отличается от ленивых списков: методы, такие как map и filter, не будут оцениваться лениво (поэтому они не будут работать с бесконечными перечислимыми объектами) и элементами, которые были рассчитаны один раз не сохраняются, поэтому, если вы дважды обращаетесь к элементу, он вычисляется дважды.

Если вы хотите точного поведения ленивых списков haskell в рубине, там lazylist gem, который реализует ленивые списки.

Ответ 6

Это приведет к бесконечности:

0.step{|i| puts i}

Это приведет к бесконечности в два раза быстрее:

0.step(nil, 2){|i| puts i}

Это перейдет в бесконечность, только если вы хотите его (результат в Enumerator).

table_of_3 = 0.step(nil, 3)

Ответ 7

Ruby Arrays динамически расширяются по мере необходимости. Вы можете применять к ним блоки, чтобы возвращать такие вещи, как четные числа.

array = []
array.size # => 0
array[0] # => nil
array[9999] # => nil
array << 1
array.size # => 1
array << 2 << 3 << 4
array.size # => 4

array = (0..9).to_a
array.select do |e|
  e % 2 == 0
end

# => [0,2,4,6,8]

Помогает ли это?