Как я могу получить ленивый массив в Ruby?
Как я могу получить ленивый массив в Ruby?
В Haskell я могу говорить о [1..]
, который представляет собой бесконечный список, лениво генерируемый по мере необходимости. Я также могу делать такие вещи, как iterate (+2) 0
, который применяет любую функцию, которую я даю ей, для создания ленивого списка. В этом случае это даст мне все четные числа.
Я уверен, что могу делать такие вещи в Ruby, но не могу понять, как это сделать.
Ответы
Ответ 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
Ответ 4
В Ruby 2.0.0 им был введен новый метод "Lazy" в классе Enumerable.
Вы можете проверить ленивое функциональное ядро и использование здесь.
http://www.ruby-doc.org/core-2.0/Enumerator/Lazy.html
https://github.com/yhara/enumerable-lazy
http://shugomaeda.blogspot.in/2012/03/enumerablelazy-and-its-benefits.html
Ответ 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]
Помогает ли это?