Map operation on sets isn’t closed, both in Python:
>>> type( map(lambda x: x**2, set([1,2,3,4])) )
<type 'list'>
and Ruby:
>> Set.new([1,2,3,4]).map { |x| x**2 }.class
=> Array
At least Haskell does the right thing:
Main> typeOf( Set.map (\x -> x^2) (Set.fromList([1,2,3,4::Integer])) )
Set Integer
An obvious workaround is to operate on lists and convert to sets once you need them. You have to ask yourself whether it will cause performance problems in your application.
Would you want it closed with a dynamic language though? Considering you can pass in *any* data type to map, as long as it supports the right methods, I don’t see how it could guarantee a closed operation.
I suppose the spec could say “generates a set when passed in a set, and a list otherwise”, but that doesn’t seem very consistent.
Actually, what I would like to see is (similar to Haskell implementation) a special version of map for sets. There is itertools.imap to return an iterator, standard map to return a list, but no version of map for sets. Having a set of map/filter/reduce optimized for use with sets would be very convenient. Sadly, I don’t have time to implement it myself, so considering pretty low importance of this feature, it probably won’t get implemented at all.
Just found out that sets support generator expressions, so I can implement map and reduce pretty easily. :-)
def smap(f, s):
return set(f(x) for x in s)
def sfilter(f, s):
if f is None:
f = lambda x: x
return set(x for x in s if f(x))
Example use:
>>> smap(lambda x: x**2, set([1,2,3,4]))
set([16, 1, 4, 9])
>>> sfilter(lambda x: x > 2, set([1,2,3,4]))
set([3, 4])
Reduce for sets will only make sense for commutative operations, so I left that one out.
Why don’t you just use itertools.imap ?
>>> from itertools import imap
>>> set(imap(lambda x: x**2, set([1,2,3,4])))
set([16, 1, 4, 9])
You’re right, itertools will do the job as well. But unless you’re using Python 2.3, the code with generator expressions simply looks better. :-)