kachayev / fn.py

Functional programming in Python: implementation of missing features to enjoy FP
Other
3.35k stars 204 forks source link

Additional functions in fn.iters #35

Closed microamp closed 10 years ago

microamp commented 10 years ago

Additional functions to be added to fn.iters (influenced by Underscore.js):

Unit tests added. Tested with Python 2 (2.7) and Python 3 (3.3).

Thanks for reviewing in advance. Any feedback, positive or negative, would be highly appreciated.

Cheers, James

microamp commented 10 years ago

Will update unit tests in order to make it compatible with Python 2.6.

microamp commented 10 years ago

Unit tests updated and build now passed with Python 2.6 as well.

kachayev commented 10 years ago

Actually, compact is not very useful. There is special filter form in Python:

In [3]: filter(None, [1,0,2,"",3,False])
Out[3]: [1, 2, 3]

which means that compact = partial(filter, None).

kachayev commented 10 years ago

reject is ok, implementation is not obvious. In any case, Haskell variant is good example of functional programming:

reject f = filter (not . f)

It will be good point to implement something like this in fn.py to give example of functional approach.

from operator import not_
from functools import partial

compact = partial(filter, None)
def reject(f, it):
    return filter(compose(not_, id if f is None else f), it)

I didn't check this code, just few ideas.

P.S. id should be defined instead of lambda x: x because it is really widespread case in functional programming.

kachayev commented 10 years ago

I didn't catch idea in groupby. For countby you can use composition of collections.Counter with map.

microamp commented 10 years ago

Thanks for the replies and suggestions, much appreciated.

How does this look for reject?

from operator import not_
from fn.func import F
from fn.underscore import shortcut as _

def reject(func, iterable):
    return filter(F(not_) << (_ if func is None else func), iterable)

even = _ % 2 == 0
assert [2, 4, 6, 8, 10] == list(filter(even, range(1, 11)))
assert [1, 3, 5, 7, 9] == list(reject(even, range(1, 11)))

Or, I can do

return filter(F(_ if func is None else func) >> not_, iterable)

Slightly more concise as it doesn't require extra parens.

P.S. I will open an issue prior to a commit request next time. Thanks, learning a lot here.

kachayev commented 10 years ago

I like first variant, you can even try to do

def reject(func, iterable):
    return filter(F(not_) << (func or _), iterable)

It doesn't check for concrete None value, but useful enough.

microamp commented 10 years ago

Nice. I will open a new pull request for reject tomorrow. Please close this one.

kachayev commented 10 years ago

Will you add compact = partial(filter, None)? And update please README file with new functions (iters block).

microamp commented 10 years ago

Okay, I will add compact as well (unless you find it trivial). And I will update README accordingly. Thanks.