Skip to content

Commit f6ab7a7

Browse files
author
czheo
committed
new lazy & connectable pipe
1 parent dce79bd commit f6ab7a7

File tree

7 files changed

+164
-256
lines changed

7 files changed

+164
-256
lines changed

README.md

Lines changed: 71 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,60 @@ Inspired by https://github.com/matz/streem.
1111
pip install syntax_sugar
1212
```
1313

14-
# Usage
15-
This is the only line you need to use this lib.
16-
```
14+
# Use
15+
16+
To test out this lib, you can simply do.
17+
18+
``` python
1719
from syntax_sugar import *
1820
```
1921

22+
For serious use, you can explicitly import each component as explained below ... if you dare to use this lib.
23+
2024
### pipe
2125
``` python
26+
from syntax_sugar import pipe, END
27+
from functools import partial
28+
29+
pipe(10) | range | partial(map, lambda x: x**2) | list | print | END
2230
# put 10 into the pipe and just let data flow.
23-
pipe(10) | range | each(lambda x: x ** 2) | print
2431
# output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
32+
# remember to call END at the end, so the pipe knows you want start the pipe
33+
# NOTE: everything in the middle of the pipe is just normal Python functions
34+
35+
pipe(10) | range | (map, lambda x: x**2) | list | print | END
36+
# Tuples are shortcuts for partial functions
2537

26-
# save the result to a var
27-
x = pipe(10) | range | each(lambda x: x ** 2) | dump()
28-
# remember to call dump at the end, so the pipe will know you want to dump the value
38+
from syntax_sugar import each
39+
x = pipe(10) | range | each(lambda x: x ** 2) | END
40+
# `each` is just a shortcut of the partial function of `map`
41+
# We can also save the result in a variable
2942

43+
pipe(10) | range | each(str) | ''.join > 'test.txt'
3044
# wanna write to a file? Why not!
31-
pipe(10) | range | (map, str) | concat > 'test.txt'
3245
# write "0123456789" to test.txt
46+
# We don't need to put END here.
47+
```
48+
49+
We can connect multiple pipes to create a longer pipe
50+
51+
``` python
52+
from syntax_sugar import pipe, each, END
53+
from functools import reduce
54+
55+
p1 = pipe(10) | range | each(lambda x: x/2)
56+
# head pipe can have input value
57+
p2 = pipe() | (reduce, lambda acc, x: (acc + x)/2)
58+
p3 = pipe() | int | range | sum
59+
# middle pipes can have no input value
60+
61+
p1 | p2 | p3 | END
62+
# returns 6
63+
64+
# you can also put a different value in the pipe
65+
p = p1 | p2 | p3
66+
p(20)
67+
# returns 36
3368
```
3469

3570
### pipe with thread/process and multiprocessing
@@ -39,28 +74,29 @@ You can have a function running in a seperate thread with pipe. Just put it in a
3974
Because of the notorious GIL(Global Interpret Lock) of Python, people may want processes instead of threads. Just put a function in `p[]`.
4075

4176
``` python
42-
pipe(10) | [print] # print run in a thread
43-
pipe(10) | t[print] # print run in a thread
44-
pipe(10) | p[print] # print run in a process
77+
from syntax_sugar import thread_syntax as t, process_syntax as p
78+
79+
pipe(10) | [print] | END # print run in a thread
80+
pipe(10) | t[print] | END # print run in a thread
81+
pipe(10) | p[print] | END # print run in a process
4582
```
4683

4784
What makes this syntax good is that you can specify how many threads you want to spawn, by doing `[function] * n` where `n` is the number of threads.
4885

4986
``` python
50-
pipe([1,2,3,4,5]) | [print] * 3 # print will run in a ThreadPool of size 3
87+
pipe([1,2,3,4,5]) | [print] * 3 | END # print will run in a ThreadPool of size 3
5188
```
5289

5390
Here is an example of requesting a list of urls in parallel
5491

5592
``` python
5693
import requests
57-
(
58-
pipe(['google', 'twitter', 'yahoo', 'facebook', 'github'])
94+
(pipe(['google', 'twitter', 'yahoo', 'facebook', 'github'])
5995
| each(lambda name: 'http://' + name + '.com')
6096
| [requests.get] * 3 # !! `requests.get` runs in a ThreadPool of size 3
6197
| each(lambda resp: (resp.url, resp.headers.get('Server')))
62-
| dump()
63-
)
98+
| list
99+
| END)
64100

65101
# returns
66102
# [('http://www.google.com/', 'gws'),
@@ -72,9 +108,14 @@ pipe(['google', 'twitter', 'yahoo', 'facebook', 'github'])
72108

73109
### infix function
74110
``` python
111+
from syntax_sugar import is_a, hasattr, to, by, drop
112+
75113
1 /is_a/ int
76114
# equivalent to `isinstance(1, int)`
77115

116+
range(10) /hasattr/ '__iter__'
117+
# equivalent to `hasattr(range(10), "__iter__")`
118+
78119
1 /to/ 10
79120
# An iterator similar to `range(1, 11)`.
80121
# Python's nasty range() is right-exclusive. This is right-inclusive.
@@ -93,9 +134,9 @@ pipe(['google', 'twitter', 'yahoo', 'facebook', 'github'])
93134
# Go backward.
94135
# Similar to `range(10, 0, -2)`
95136

96-
'A' /to/ 'Z' /by/ 3
97-
# Also works with characters with /by/.
98-
# An iterator similar to 'ADGJMPSVY'
137+
1 /to/ 10 /drop/ 5
138+
# there is a `drop` functon which drop N items from the head
139+
# An iterator similar to [6, 7, 8, 9, 10]
99140
```
100141

101142
`/to/` has some advanced features
@@ -106,34 +147,34 @@ pipe(['google', 'twitter', 'yahoo', 'facebook', 'github'])
106147
- support pipe.
107148

108149
``` python
150+
from syntax_sugar import INF, NEGINF, take, each
151+
109152
# CAUTION: this will infinitely print numbers
110153
for i in 1 /to/ INF:
111154
print(i)
112155

113-
print(1 /to/ INF /take/ 5)
156+
list(1 /to/ INF /take/ 5)
114157
# there is a `take` functon which is similar to itertools.islice
115158
# return [1, 2, 3, 4, 5]
116159

117-
print(0 /to/ NEGINF /by/ 2 /take/ 5)
160+
list(0 /to/ NEGINF /by/ 2 /take/ 5)
118161
# also works with negative infinity.
119162
# return [0, -2, -4, -6, -8]
120163

121-
print(1 /to/ 10 /drop/ 5)
122-
# there is a `drop` functon which drop N items from the head
123-
# return [6, 7, 8, 9, 10]
124-
125-
# print all combinations of [1..3] * [4..6]
126-
print([(x, y) for x, y in (1 /to/ 3) * (4 /to/ 6)])
164+
list((1 /to/ 3) * (4 /to/ 6))
165+
# all combinations of [1..3] * [4..6]
127166
# return [(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]
128167

129-
# Now, these infix functions can also be piped
130-
1 /to/ 10 /take/ 5 | each(lambda x: x **2) | print
168+
1 /to/ 10 /take/ 5 | each(lambda x: x **2) | list | END
169+
# These infix functions can also be piped.
131170
# [1, 4, 9, 16, 25]
132171
```
133172

134173
Make your own infix function, so you can append multiple items to a list in one line.
135174

136175
``` python
176+
from syntax_sugar import infix
177+
137178
@infix
138179
def push(lst, x):
139180
lst.append(x)
@@ -143,78 +184,4 @@ def push(lst, x):
143184
# returns [1,2,3]
144185
```
145186

146-
### lazy pipe
147-
148-
Because our `pipe` is quite hard working, it doesn't work well with the lazy `/to/` function.
149-
150-
`lazy_pipe` works in a "one-by-one" manner. It puts only 1 item into the pipe at each time.
151-
152-
``` python
153-
lazy_pipe(1 /to/ INF) | print | when(lambda x: x < 10)
154-
# this prints 1 to 9
155-
156-
lazy_pipe(input) | (lambda s: s.upper())| print | when(lambda x: x != "")
157-
# lazy_pipe also accepts a function as input. The function is called each time as long as the `when` condition holds.
158-
# this changes anything you input to uppercases.
159-
```
160-
161-
TODO: support multiprocessing with lazy pipe
162-
163-
### composable function
164-
165-
In math, `(f * g) (x) = f(g(x))`. This is called function composition.
166-
167-
``` python
168-
# this transfer a map object to list
169-
lmap = compose(list, map)
170-
# lmap equivalent to `list(map(...))`
171-
lmap(lambda x: x ** 2, range(10))
172-
```
173-
174-
Let's say we want to represent `f * g * h` in a program, i.e. `fn(x) = f(g(h(x)))`
175-
176-
``` python
177-
f = lambda x: x**2 + 1
178-
g = lambda x: 2*x - 1
179-
h = lambda x: -2 * x**3 + 3
180-
181-
fn = compose(f, g, h)
182-
183-
fn(5) # 245026
184-
```
185-
186-
or you can do
187-
188-
```python
189-
f = composable(lambda x: x**2 + 1)
190-
g = composable(lambda x: 2*x - 1)
191-
h = composable(lambda x: -2 * x**3 + 3)
192-
193-
fn = f * g * h
194-
195-
fn(5) # 245026
196-
```
197-
198-
Some times you may prefer the decorator way.
199-
200-
``` python
201-
# make your own composable functions
202-
@composable
203-
def add2(x):
204-
return x + 2
205-
206-
@composable
207-
def mul3(x):
208-
return x * 3
209-
210-
@composable
211-
def pow2(x):
212-
return x ** 2
213-
214-
fn = add2 * mul3 * pow2
215-
# equivalent to `add2(mul3(pow2(n)))`
216-
fn(5)
217-
# returns 5^2 * 3 + 2 = 77
218-
```
219-
220-
more receipes: https://github.com/czheo/syntax_sugar_python/tree/master/recipes
187+
More receipes: https://github.com/czheo/syntax_sugar_python/tree/master/recipes

recipes/cat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
from functools import partial
44

55

6-
pipe(argv[1:]) | each(lambda filename: open(filename).read()) | concat | partial(print, end='')
6+
pipe(argv[1:]) | each(lambda filename: open(filename).read()) | ''.join | partial(print, end='') | END

recipes/echo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from syntax_sugar import *
22
from sys import argv
33

4-
pipe(argv[1:]) | ' '.join | print
4+
pipe(argv[1:]) | ' '.join | print | END

recipes/mapred.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from syntax_sugar import *
22
from functools import reduce
33

4-
(
5-
pipe(10) | range
6-
| (map, lambda x: x ** 2)
7-
| sum
8-
| print
9-
)
4+
(pipe(10)
5+
| range
6+
| (map, lambda x: x ** 2)
7+
| sum
8+
| print
9+
| END)

0 commit comments

Comments
 (0)