Lazy array

A

Andrew Wagner

[Note: parts of this message were removed to make it a legal post.]

How can I get a lazy array in Ruby? E.g., in Haskell, I can talk about
[1..], which is an infinite list, lazily generated as needed. I can also do
things like "iterate (+2) 0", which applies whatever function I give it to
generate a lazy list. In this case, it would give me all even numbers.
Anyway, I'm sure I can do such things in Ruby, but can't seem to work out
how.
 
S

skim

[Note: parts of this message were removed to make it a legal post.]

Ruby Arrays dynamically expand as needed. You can apply blocks to them to
return things like even numbers.

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]

Does this help?
 
J

jeremy Ruten

Ruby 1.9 has the Enumerator class, which can act like a lazy list I
think. A good example is given in the documentation:

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

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

Also, this is a nice trick:

Infinity =3D 1.0/0

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

This one only works for consecutive values though.

jeremy

Ruby Arrays dynamically expand as needed. =A0You can apply blocks to them= to
return things like even numbers.

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

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

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

Does this help?

How can I get a lazy array in Ruby? E.g., in Haskell, I can talk about
[1..], which is an infinite list, lazily generated as needed. I can also= do
things like "iterate (+2) 0", which applies whatever function I give it = to
generate a lazy list. In this case, it would give me all even numbers.
Anyway, I'm sure I can do such things in Ruby, but can't seem to work ou= t
how.
 
B

Benoit Daloze

Ruby 1.9 has the Enumerator class, which can act like a lazy list I
think. A good example is given in the documentation:

=A0fib =3D Enumerator.new { |y|
=A0 =A0a =3D b =3D 1
=A0 =A0loop {
=A0 =A0 =A0y << a
=A0 =A0 =A0a, b =3D b, a + b
=A0 =A0}
=A0}

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

Also, this is a nice trick:

=A0Infinity =3D 1.0/0

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

This one only works for consecutive values though.

jeremy

Ruby Arrays dynamically expand as needed. =A0You can apply blocks to the= m to
return things like even numbers.

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

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

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

Does this help?

How can I get a lazy array in Ruby? E.g., in Haskell, I can talk about
[1..], which is an infinite list, lazily generated as needed. I can als= o do
things like "iterate (+2) 0", which applies whatever function I give it= to
generate a lazy list. In this case, it would give me all even numbers.
Anyway, I'm sure I can do such things in Ruby, but can't seem to work o= ut
how.

So, with Enumerator it should be:

even =3D Enumerator.new { |y|
i =3D 0
loop {
y << i
i +=3D 2
}
}

p even.take(5) # =3D> [0, 2, 4, 6, 8]

But that is not really common practice in Ruby (Enumerator for so
simple things).

You would probably need something like (0..max).step(2) { |even_n| p even_n=
}

B.D.
 
B

Brian Candler

Benoit said:
So, with Enumerator it should be:

even = Enumerator.new { |y|
i = 0
loop {
y << i
i += 2
}
}

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

But that is not really common practice in Ruby (Enumerator for so
simple things).

In any case, that Enumerator block form is only syntactic sugar. You can
do the same without using the Enumerator class:

even = Object.new
def even.each(&blk)
(2..1.0/0).step(2,&blk)
end
even.extend Enumerable
p even.take(5)

In both cases, all you're doing is having 'each' generate an infinite
series, and then truncating it with 'take'.

This is not useful normally, because you can't chain it - most of the
Enumerable functions like 'map', 'select' etc collect the full results
into an Array before continuing.

However, it's quite possible to have these functions process one element
at a time without generating the intermediate arrays, and there is an
implementation of this in the 'facets' library:

=> [102, 104, 106, 108, 110, 112, 114, 116, 118, 120]


Infinite lists are no problem here - each element propagates through the
whole chain before the next. You're processing 'horizontally' rather
than 'vertically'.

Note that this doesn't use Threads or Fibers, it's efficient, and it
works in ruby 1.8. For the implementation details, see the Denumerator
class.
 
D

David Masover

How can I get a lazy array in Ruby? E.g., in Haskell, I can talk about
[1..], which is an infinite list, lazily generated as needed. I can also do
things like "iterate (+2) 0", which applies whatever function I give it to
generate a lazy list. In this case, it would give me all even numbers.
Anyway, I'm sure I can do such things in Ruby, but can't seem to work out
how.

In your specific case, you could do this:

list = (1..(1.0/0))

But you can't necessarily iterate it like you want, so no, Ruby does not have
an equivalent feature. You can do things like this:

list.first(10).map{|x| x*2}

That would give you the first ten even numbers, but it wouldn't work the way
you expect -- first(10) actually dumps those into an array, and map
immediately creates a new array from that one. It's not at all like the
Haskell/functional concept.

So, you can do similar and interesting things, and you can probably get close
to the semantics (roughly), but the implementation is going to be very
imperative/eager, and not at all lazy. You can do lazy-ish transformations by
writing your own Enumerators, but those aren't as elegant to write.

I think this is a solvable problem, even solvable within Ruby, I just haven't
really seen it done, and there doesn't seem to be anything in the core
language that really does it.
 
R

Robert Klemme

2010/8/2 Brian Candler said:
Benoit said:
So, with Enumerator it should be:

even =3D Enumerator.new { |y|
=A0 i =3D 0
=A0 loop {
=A0 =A0 y << i
=A0 =A0 i +=3D 2
=A0 }
}

p even.take(5) # =3D> [0, 2, 4, 6, 8]

But that is not really common practice in Ruby (Enumerator for so
simple things).

In any case, that Enumerator block form is only syntactic sugar. You can
do the same without using the Enumerator class:

=A0even =3D Object.new
=A0def even.each(&blk)
=A0 =A0(2..1.0/0).step(2,&blk)
=A0end
=A0even.extend Enumerable
=A0p even.take(5)

Even simpler:

irb(main):014:0> even =3D (2..1.0/0).step(2)
=3D> #<Enumerator:0x1027efc4>
irb(main):015:0> even.take 5
=3D> [2.0, 4.0, 6.0, 8.0, 10.0]

irb(main):016:0> even =3D 2.step(1/0.0, 2)
=3D> #<Enumerator:0x102a070c>
irb(main):017:0> even.take 5
=3D> [2.0, 4.0, 6.0, 8.0, 10.0]

(pun intended)
In both cases, all you're doing is having 'each' generate an infinite
series, and then truncating it with 'take'.

This is not useful normally, because you can't chain it - most of the
Enumerable =A0functions like 'map', 'select' etc collect the full results
into an Array before continuing.

However, it's quite possible to have these functions process one element
at a time without generating the intermediate arrays, and there is an
implementation of this in the 'facets' library:

}.take(10).to_a
=3D> [102, 104, 106, 108, 110, 112, 114, 116, 118, 120]


Infinite lists are no problem here - each element propagates through the
whole chain before the next. You're processing 'horizontally' rather
than 'vertically'.

Note that this doesn't use Threads or Fibers, it's efficient, and it
works in ruby 1.8. For the implementation details, see the Denumerator
class.

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
C

Christopher Dicely

How can I get a lazy array in Ruby? E.g., in Haskell, I can talk about
[1..], which is an infinite list, lazily generated as needed. I can also do
things like "iterate (+2) 0", which applies whatever function I give it to
generate a lazy list. In this case, it would give me all even numbers.
Anyway, I'm sure I can do such things in Ruby, but can't seem to work out
how.

The Ruby equivalent of a lazy array is an enumerator. In Ruby 1.9.2,
you can get an infinite list of positive integers as:

(1..Float::INFINITY).to_enum,

For non-negative even integers, you could do:
(0..Float::INFINITY).step(2)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,141
Latest member
BlissKeto
Top