Monday, February 21, 2011

ruby list comprehension

I have seen list comprehension cited as a feature of python that ruby lacks. I want to play around with the idea to see where it goes...

[disclaimer: this is just scratch space for ideas at the moment]

(I wonder how lazy enumerators will change the ruby approach?)

A nice write up on list comprehensions in python has an example where you can use list comprehension to find prime numbers. They divide it into two steps because it gets a little hairy on one line. Here's finding primes on one line using list comprehension:


>>> [x for x in range(2, 50) if x not in [j for i in range(2, 8) for j in range(i*2, 50, i)]]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

It's not bad, but it's easy to get lost in there (my ruby eyes are not highly trained in python, of course).

Now, one of the things list comprehensions do is it does selection and modification in the same phrase. This part of list comprehensions is easy to implement.

[ add the equivalent python here ]


(2..10).select(&:even?).map {|v| v**2 }
(2..10).map {|v| v**2 if v.even? }.compact

Implementing this aspect of list comprehensions in ruby is less than trivial:


module Enumerable
  def lc(&block)
    block.nil? ? self : self.map(&block).compact
  end
end

The other part of list comprehensions is the fact that the evaluation happens from right to left (in the python code) so that the execution of the code is efficient.

This gets the job done, but the right-most block is executed multiple times to make it work:

(2..50).reject {|j| (2..8).map {|i| (i*2).step(50,i).to_a}.flatten.include?(j) 

# also other ways to think about this (need to finish this post)
Set.new(2..50) - (2..8).reduce(Set.new) {|np,i| np.merge((i*2).step(50,i)) }

No comments: