Skip to content

Commit cb7495a

Browse files
author
czheo
committed
Refactoring and make infix functions return Iterators.
1 parent a3fe6b4 commit cb7495a

File tree

4 files changed

+221
-179
lines changed

4 files changed

+221
-179
lines changed

syntax_sugar/infix.py

Lines changed: 38 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
1-
from functools import partial, reduce
2-
from itertools import product, islice
3-
from .util import flip
4-
from .composable import compose
1+
from functools import partial
2+
from .iter import Iterator, Range
3+
4+
__all__ = [
5+
'infix',
6+
'is_a',
7+
'to',
8+
'by',
9+
'INF',
10+
'NEGINF',
11+
'hasattr',
12+
'take',
13+
'drop',
14+
]
15+
16+
INF = float('inf')
17+
NEGINF = float('-inf')
518

619
class infix(partial):
720
def __truediv__(self, right):
@@ -10,130 +23,32 @@ def __truediv__(self, right):
1023
def __rtruediv__(self, left):
1124
return infix(self.func, left)
1225

13-
is_a = of = infix(isinstance)
14-
contains = infix(lambda lst, item: item in lst)
15-
pair = infix(lambda a, b: (a, b))
16-
join = infix(lambda lst, s: s.join(lst))
26+
is_a = infix(isinstance)
1727
hasattr = infix(hasattr)
18-
fmap = infix(flip(map))
19-
ffilter = infix(flip(filter))
20-
freduce = infix(flip(reduce))
21-
take = infix(compose(list, islice))
22-
drop = infix(lambda seq, idx: islice(seq, idx, None))
23-
24-
INF = float('inf')
25-
NEGINF = float('-inf')
26-
27-
class To:
28-
def __init__(self, start, end):
29-
if start in {INF, NEGINF}:
30-
raise ValueError('Cannot start range from infinity')
31-
32-
valid_char = lambda c: c /of/ str and len(c) == 1
33-
valid_integer = lambda i: i /of/ int or i == INF or i == NEGINF
34-
35-
if valid_integer(start) and valid_integer(end):
36-
self.type = 'number'
37-
elif valid_char(start) and valid_char(end):
38-
self.type = 'char'
39-
else:
40-
raise TypeError('Unknown range: %s to %s' % (start, end))
41-
42-
self.start = start
43-
self.curr = self.start
44-
self._step = 1 if end > start else -1
45-
self.end = end
46-
47-
@property
48-
def step(self):
49-
return self._step
50-
51-
@step.setter
52-
def step(self, value):
53-
if not value /is_a/ int:
54-
raise TypeError('Step must be int')
55-
elif value == 0:
56-
raise ValueError('Step cannot be zero')
57-
elif self.start < self.end and value < 0:
58-
raise ValueError('Increasing range with negative step')
59-
elif self.start > self.end and value > 0:
60-
raise ValueError('Decreasing range with positive step')
61-
62-
self._step = value
63-
64-
def __mul__(self, rhs):
65-
return product(self, rhs)
66-
67-
def __iter__(self):
68-
return self
69-
70-
def __next__(self):
71-
def next_number():
72-
too_big = self.step > 0 and self.curr > self.end
73-
too_small = self.step < 0 and self.curr < self.end
74-
75-
if too_big or too_small: raise StopIteration
76-
77-
ret = self.curr
78-
self.curr += self.step
79-
return ret
80-
81-
def next_char():
82-
too_big = self.step > 0 and ord(self.curr) > ord(self.end)
83-
too_small = self.step < 0 and ord(self.curr) < ord(self.end)
84-
85-
if too_big or too_small: raise StopIteration
86-
87-
ret = self.curr
88-
self.curr = chr(ord(self.curr) + self.step)
89-
return ret
90-
91-
if self.type == 'number':
92-
return next_number()
93-
elif self.type == 'char':
94-
return next_char()
95-
else:
96-
raise StopIteration
97-
98-
def __str__(self):
99-
if self.type == 'number':
100-
return super(To, self).__str__()
101-
elif self.type == 'char':
102-
return ''.join(self)
103-
else:
104-
raise NotImplementedError
10528

10629
@infix
10730
def to(start, end):
108-
return To(start, end)
31+
return Iterator(Range(start, end))
10932

11033
@infix
111-
def by(to_object, step):
112-
if to_object.end >= to_object.start and step < 0:
113-
to_object.step = -step
114-
elif to_object.end <= to_object.start and step > 0:
115-
to_object.step = -step
116-
else:
117-
to_object.step = step
118-
119-
return to_object
34+
def by(obj, step):
35+
# ex. -2 step -> 2 step
36+
step = abs(step)
37+
obj = Iterator(obj) if not obj /is_a/ Iterator else obj
38+
return obj.slice(step=step)
12039

40+
@infix
41+
def take(obj, n):
42+
# ex. n = 3
43+
# [0, 1, 2, 3, 4, 5, 6] =>
44+
# [0, 1, 2]
45+
obj = Iterator(obj) if not obj /is_a/ Iterator else obj
46+
return obj.slice(stop=n)
12147

122-
__all__ = [
123-
'infix',
124-
'of',
125-
'is_a',
126-
'contains',
127-
'pair',
128-
'join',
129-
'to',
130-
'by',
131-
'INF',
132-
'NEGINF',
133-
'hasattr',
134-
'fmap',
135-
'freduce',
136-
'ffilter',
137-
'take',
138-
'drop',
139-
]
48+
@infix
49+
def drop(obj, n):
50+
# ex. n = 3
51+
# [0, 1, 2, 3, 4, 5, 6] =>
52+
# [3, 4, 5, 6]
53+
obj = Iterator(obj) if not obj /is_a/ Iterator else obj
54+
return obj.slice(start=n)

syntax_sugar/iter.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
from .pipe import pipe
2+
from itertools import product, islice
3+
4+
INF = float('inf')
5+
NEGINF = float('-inf')
6+
7+
class Iterator:
8+
def __init__(self, data):
9+
if hasattr(data, '__iter__'):
10+
self.data = iter(data)
11+
else:
12+
raise TypeError('input must be iterable data')
13+
14+
def __or__(self, rhs):
15+
return pipe(self) | rhs
16+
17+
def __iter__(self):
18+
return self
19+
20+
def __mul__(self, rhs):
21+
self.data = product(self, rhs)
22+
return self
23+
24+
def __next__(self):
25+
return next(self.data)
26+
27+
def slice(self, start=0, stop=None, step=1):
28+
self.data = islice(self.data, start, stop, step)
29+
return self
30+
31+
class Range:
32+
def __init__(self, start, end):
33+
if start in {INF, NEGINF}:
34+
raise ValueError('Cannot start range from infinity')
35+
36+
valid_char = lambda c: isinstance(c, str) and len(c) == 1
37+
valid_integer = lambda i: isinstance(i, int) or i == INF or i == NEGINF
38+
39+
if valid_integer(start) and valid_integer(end):
40+
self.type = 'number'
41+
elif valid_char(start) and valid_char(end):
42+
self.type = 'char'
43+
else:
44+
raise TypeError('Unknown range: %s to %s' % (start, end))
45+
46+
self.start = start
47+
self.curr = self.start
48+
self._step = 1 if end > start else -1
49+
self.end = end
50+
51+
@property
52+
def step(self):
53+
return self._step
54+
55+
@step.setter
56+
def step(self, value):
57+
if not isinstance(value, int):
58+
raise TypeError('Step must be int')
59+
elif value == 0:
60+
raise ValueError('Step cannot be zero')
61+
elif self.start < self.end and value < 0:
62+
raise ValueError('Increasing range with negative step')
63+
elif self.start > self.end and value > 0:
64+
raise ValueError('Decreasing range with positive step')
65+
66+
self._step = value
67+
68+
def __iter__(self):
69+
return self
70+
71+
def __next__(self):
72+
def next_number():
73+
too_big = self.step > 0 and self.curr > self.end
74+
too_small = self.step < 0 and self.curr < self.end
75+
76+
if too_big or too_small: raise StopIteration
77+
78+
ret = self.curr
79+
self.curr += self.step
80+
return ret
81+
82+
def next_char():
83+
too_big = self.step > 0 and ord(self.curr) > ord(self.end)
84+
too_small = self.step < 0 and ord(self.curr) < ord(self.end)
85+
86+
if too_big or too_small: raise StopIteration
87+
88+
ret = self.curr
89+
self.curr = chr(ord(self.curr) + self.step)
90+
return ret
91+
92+
if self.type == 'number':
93+
return next_number()
94+
elif self.type == 'char':
95+
return next_char()
96+
else:
97+
raise StopIteration
98+
99+
def __str__(self):
100+
if self.type == 'number':
101+
return super(Range, self).__str__()
102+
elif self.type == 'char':
103+
return ''.join(self)
104+
else:
105+
raise NotImplementedError

0 commit comments

Comments
 (0)