Как я могу получить ленивый массив в Ruby?
В Haskell я могу говорить о [1..]
, который является бесконечным списком, лениво сгенерированным по мере необходимости. Я могу также сделать вещи как iterate (+2) 0
, который применяет любую функцию, я даю ее для генерации ленивого списка. В этом случае это дало бы мне все четные числа.
Я уверен, что могу сделать такие вещи в Ruby, но, может казаться, не удаюсь как.
В 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]
Правда, он работает только для последовательных значений.
Ленивый диапазон (натуральные числа):
Inf = 1.0/0.0
(1..Inf).take(3) #=> [1, 2, 3]
Ленивый диапазон (четные числа):
(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
Как я уже сказал в своих комментариях, реализация такой вещи, как ленивые массивы, не будет разумной.
Использование Enumerable вместо этого может хорошо работать в некоторых ситуациях, но отличается от ленивых списков в некоторых моментах: такие методы как map и filter не будут лениво оцениваться (поэтому они не будут работать на бесконечных перечислителях), а элементы, которые были вычислены один раз, не сохраняются, поэтому если вы обращаетесь к элементу дважды, он будет вычислен дважды.
Если вы хотите получить точное поведение ленивых списков haskell в ruby, существует lazylist gem, который реализует ленивые списки.
Массивы Ruby динамически расширяются по мере необходимости. Вы можете применять к ним блоки, чтобы возвращать такие вещи, как четные числа.
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]
Это поможет?