-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpythonic_pipes.py
222 lines (164 loc) · 4.63 KB
/
pythonic_pipes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
"""
Some utilities for functional programming.
Functions allow the currying of operators and other
functions including generator expressions for use in
pipelines of data processing.
The curried operators and functions take the role of
functions mapped onto the data, or as the criteria
for filtering, or both.
The curried generator expressions take the role of
the map() and filter() functions.
"""
import itertools
import functools
def is_not_in(y):
"""
Return function that tests if x is not in the specified y.
Example:
>>> nums = [1, 2, 3, 4]
>>> test = is_not_in(nums)
>>> test(5)
True
"""
def is_not(x):
return x not in y
return is_not
def is_in(y):
"""
Return function that tests if x is in the specified y.
Example:
>>> nums = [1, 2, 3, 4]
>>> test = is_in(nums)
>>> test(2)
True
"""
def is_in(x):
return x in y
return is_in
def to_power(power):
"""
Return a function that raises a number to the specified power.
Example: raise to power 3
>>> pow_three = to_power(3)
>>> pow_three(2)
8
>>> pow3(5)
125
"""
def nth_power(number):
return number ** power
return nth_power
def use_operator(o, x):
"""
Return a curried version of the specified operator, that
applies x to every supplied y
Examples: add 3; subtract 7
>>> from operator import add, sub
>>> add3 = use_operator(add, 3)
>>> add3(10)
10
>>> sub7 = use_operator(sub, 7)
>>> sub7(10)
3
"""
def oper(y):
return o(y, x)
return oper
def map_over(func):
"""
Return generator that applies a function to all elements.
Works like map()
Example: generator to add 3 to every element
>>> from operator import add
>>> nums = [1, 2, 3, 4]
>>> add3 = use_operator(add, 3)
>>> add_three = map_over(add3)
>>> nums_plus_three = (add_three(nums))
>>> list(nums_plus_three)
[4, 5, 6, 7]
"""
def generator(data):
return (func(x) for x in data)
return generator
def filter_by(func):
"""
Return generator that filters all elements by func.
Works like filter()
Example: return only odd numbers
>>> is_odd = lambda x: x % 2 != 0
>>> nums = [1, 2, 3, 4, 5]
>>> odd = filter_by(is_odd)
>>> odd_nums = (odd(nums))
>>> list(odd_nums)
[1, 3, 5]
"""
def generator(data):
return (x for x in data if func(x))
return generator
def map_over_filter_by(map_func, filter_func):
"""
Return generator that maps a function only to those
elements filtered by some criteria, leaving the other
elements intact.
Example: add 100 to all the odd numbers
>>> from operator import add
>>> is_odd = lambda x: x % 2 != 0
>>> add100 = use_operator(add, 100)
>>> nums = [1, 2, 3, 4, 5]
>>> add_100_to_odd = map_over_filter_by(add100, is_odd)
>>> odd_plus_100 = (add_100_to_odd(nums))
>>> list(odd_plus_100)
[101, 2, 103, 4, 105]
"""
def generator(data):
return (map_func(x) if filter_func(x) else x for x in data)
return generator
def take(n, iterator):
""" Return the first n elements. """
return itertools.islice(iterator, n)
def drop(n, it):
""" Return all but the first n elements """
return itertools.islice(it, n, None)
def tail(it):
""" Return all but the first element. """
t = functools.partial(drop, 1)
return t(it)
def iterate(f, x):
"""
Applies f to x, then f(f(x)), then f(f(f(x))), etc.
Example: apply double three times to an x of 2
>>> double = lambda x: x + x
>>> take(4, iterate(double, 2))
[2, 4, 8, 16]
"""
return itertools.accumulate(itertools.repeat(x), lambda fx, _: f(fx))
def compose(f, g):
"""
Return a function that applies two functions.
Functions are applied from right to left, i.e. in
reverse order to which they are input.
Example: raise a number to **2 and add 100
>>> num = 3
>>> add100 = use_operator(add, 100)
>>> pow2 = to_power(2)
>>> new_num = compose(add_100, pow2)
>>> new_num(3)
109
"""
def composed(x):
return f(g(x))
return composed
def compose_many(*funcs):
"""
Returns a function that applies multiple functions.
Extends compose to cases with more than two functions.
Example: raise to **2, add 100, subtract 20
>>> num = 3
>>> sub20 = use_operator(sub, 20)
>>> add100 = use_operator(add, 100)
>>> pow2 = to_power(2)
>>> new_num = compose_many(sub20, add_100, pow2)
>>> new_num(3)
89
"""
return functools.reduce(compose, funcs)